diff options
Diffstat (limited to 'source3/lib/sids.c')
-rw-r--r-- | source3/lib/sids.c | 554 |
1 files changed, 554 insertions, 0 deletions
diff --git a/source3/lib/sids.c b/source3/lib/sids.c new file mode 100644 index 0000000000..432c3349e3 --- /dev/null +++ b/source3/lib/sids.c @@ -0,0 +1,554 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + Samba utility functions + Copyright (C) Andrew Tridgell 1992-1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + + +extern int DEBUGLEVEL; +extern pstring scope; +extern pstring global_myname; + +/* + * This is set on startup - it defines the SID for this + * machine, and therefore the SAM database for which it is + * responsible. + */ + +DOM_SID global_sam_sid; + +/* + * This is the name associated with the SAM database for + * which this machine is responsible. In the case of a PDC + * or PDC, this name is the same as the workgroup. In the + * case of "security = domain" mode, this is the same as + * the name of the server (global_myname). + */ + +fstring global_sam_name; + +/* + * This is obtained on startup - it defines the SID for which + * this machine is a member. It is therefore only set, and + * used, in "security = domain" mode. + */ + +DOM_SID global_member_sid; + +/* + * note the lack of a "global_member_name" - this is because + * this is the same as "global_myworkgroup". + */ + +extern fstring global_myworkgroup; +/* fstring global_member_dom_name; */ + +/* + * some useful sids + */ + +DOM_SID global_sid_S_1_5_20; /* local well-known domain */ +DOM_SID global_sid_S_1_1; /* everyone */ +DOM_SID global_sid_S_1_3; /* Creator Owner */ +DOM_SID global_sid_S_1_5; /* NT Authority */ + +static struct sid_name_map_info +{ + DOM_SID *sid; + char *name; + +} +sid_name_map[] = +{ + { &global_sid_S_1_5_20, "BUILTIN" }, + { &global_sid_S_1_1 , "Everyone" }, + { &global_sid_S_1_3 , "Creator Owner" }, + { &global_sid_S_1_5 , "NT Authority" }, + { &global_sam_sid , global_sam_name }, + { &global_member_sid , global_myworkgroup }, + { NULL , NULL } +}; + +/**************************************************************************** + Read the machine SID from a file. +****************************************************************************/ + +static BOOL read_sid_from_file(int fd, char *sid_file) +{ + fstring fline; + fstring sid_str; + + memset(fline, '\0', sizeof(fline)); + + if (read(fd, fline, sizeof(fline) -1 ) < 0) { + DEBUG(0,("unable to read file %s. Error was %s\n", + sid_file, strerror(errno) )); + return False; + } + + /* + * Convert to the machine SID. + */ + + fline[sizeof(fline)-1] = '\0'; + if (!string_to_sid( &global_sam_sid, fline)) { + DEBUG(0,("unable to generate machine SID.\n")); + return False; + } + + sid_to_string(sid_str, &global_sam_sid); + DEBUG(5,("read_sid_from_file: sid %s\n", sid_str)); + + return True; +} + +/**************************************************************************** + sets up the name associated with the SAM database for which we are responsible +****************************************************************************/ +void get_sam_domain_name(void) +{ + switch (lp_server_role()) + { + case ROLE_DOMAIN_PDC: + case ROLE_DOMAIN_BDC: + { + /* we are PDC (or BDC) for a Domain */ + fstrcpy(global_sam_name, lp_workgroup()); + break; + } + case ROLE_DOMAIN_MEMBER: + { + /* we are a "PDC", but FOR LOCAL SAM DATABASE ONLY */ + fstrcpy(global_sam_name, global_myname); + break; + } + default: + { + /* no domain role, probably due to "security = share" */ + memset(global_sam_name, 0, sizeof(global_sam_name)); + break; + } + } +} + +/**************************************************************************** + obtain the sid from the PDC. do some verification along the way... +****************************************************************************/ +BOOL get_member_domain_sid(void) +{ + POLICY_HND pol; + fstring srv_name; + struct cli_state cli; + BOOL res = True; + DOM_SID sid3; + DOM_SID sid5; + fstring dom3; + fstring dom5; + + switch (lp_server_role()) + { + case ROLE_DOMAIN_NONE: + { + ZERO_STRUCT(global_member_sid); + return True; + } + case ROLE_DOMAIN_PDC: + { + sid_copy(&global_member_sid, &global_sam_sid); + return True; + } + default: + { + /* member or BDC, we're going for connection to PDC */ + break; + } + } + + if (!cli_connect_serverlist(&cli, lp_passwordserver())) + { + DEBUG(0,("get_member_domain_sid: unable to initialise client connection.\n")); + return False; + } + + /* + * Ok - we have an anonymous connection to the IPC$ share. + * Now start the NT Domain stuff :-). + */ + + fstrcpy(dom3, ""); + fstrcpy(dom5, ""); + ZERO_STRUCT(sid3); + ZERO_STRUCT(sid5); + + fstrcpy(srv_name, "\\\\"); + fstrcat(srv_name, global_myname); + strupper(srv_name); + + /* open LSARPC session. */ + res = res ? cli_nt_session_open(&cli, PIPE_LSARPC) : False; + + /* lookup domain controller; receive a policy handle */ + res = res ? do_lsa_open_policy(&cli, srv_name, &pol, False) : False; + + /* send client info query, level 3. receive domain name and sid */ + res = res ? do_lsa_query_info_pol(&cli, &pol, 3, dom3, &sid3) : False; + + /* send client info query, level 5. receive domain name and sid */ + res = res ? do_lsa_query_info_pol(&cli, &pol, 5, dom5, &sid5) : False; + + /* close policy handle */ + res = res ? do_lsa_close(&cli, &pol) : False; + + /* close the session */ + cli_nt_session_close(&cli); + cli_ulogoff(&cli); + cli_shutdown(&cli); + + if (res) + { + pstring sid; + DEBUG(2,("LSA Query Info Policy\n")); + sid_to_string(sid, &sid3); + DEBUG(2,("Domain Member - Domain: %s SID: %s\n", dom3, sid)); + sid_to_string(sid, &sid5); + DEBUG(2,("Domain Controller - Domain: %s SID: %s\n", dom5, sid)); + + if (!strequal(dom3, global_myworkgroup) || + !strequal(dom5, global_myworkgroup)) + { + DEBUG(0,("get_member_domain_sid: %s is a DC for %s not %s\n", + cli.desthost, dom5, global_myworkgroup)); + res = False; + } + } + else + { + DEBUG(1,("lsa query info failed\n")); + } + + if (!res) + { + DEBUG(0,("get_member_domain_sid: unable to obtain Domain member SID\n")); + } + else + { + /* this is a _lot_ of trouble to go to for just this info: */ + global_member_sid = sid5; + } + + return res; +} + +/**************************************************************************** + creates some useful well known sids +****************************************************************************/ +void generate_wellknown_sids(void) +{ + string_to_sid(&global_sid_S_1_5_20, "S-1-5-32"); + string_to_sid(&global_sid_S_1_1 , "S-1-1" ); + string_to_sid(&global_sid_S_1_3 , "S-1-3" ); + string_to_sid(&global_sid_S_1_5 , "S-1-5" ); +} + +/**************************************************************************** + Generate the global machine sid. Look for the MACHINE.SID file first, if + not found then look in smb.conf and use it to create the MACHINE.SID file. +****************************************************************************/ +BOOL generate_sam_sid(void) +{ + int fd; + int i; + char *p; + pstring sid_file; + fstring sid_string; + SMB_STRUCT_STAT st; + uchar raw_sid_data[12]; + + pstrcpy(sid_file, lp_smb_passwd_file()); + p = strrchr(sid_file, '/'); + if (p != NULL) { + *++p = '\0'; + } + + if (!directory_exist(sid_file, NULL)) { + if (dos_mkdir(sid_file, 0700) != 0) { + DEBUG(0,("can't create private directory %s : %s\n", + sid_file, strerror(errno))); + return False; + } + } + + pstrcat(sid_file, "MACHINE.SID"); + + if ((fd = sys_open(sid_file, O_RDWR | O_CREAT, 0644)) == -1) { + DEBUG(0,("unable to open or create file %s. Error was %s\n", + sid_file, strerror(errno) )); + return False; + } + + /* + * Check if the file contains data. + */ + + if (sys_fstat( fd, &st) < 0) { + DEBUG(0,("unable to stat file %s. Error was %s\n", + sid_file, strerror(errno) )); + close(fd); + return False; + } + + if (st.st_size > 0) { + /* + * We have a valid SID - read it. + */ + if (!read_sid_from_file( fd, sid_file)) { + DEBUG(0,("unable to read file %s. Error was %s\n", + sid_file, strerror(errno) )); + close(fd); + return False; + } + close(fd); + return True; + } + + /* + * Generate the new sid data & turn it into a string. + */ + generate_random_buffer( raw_sid_data, 12, True); + + fstrcpy( sid_string, "S-1-5-21"); + for( i = 0; i < 3; i++) { + fstring tmp_string; + slprintf( tmp_string, sizeof(tmp_string) - 1, "-%u", IVAL(raw_sid_data, i*4)); + fstrcat( sid_string, tmp_string); + } + + fstrcat(sid_string, "\n"); + + /* + * Ensure our new SID is valid. + */ + + if (!string_to_sid( &global_sam_sid, sid_string)) { + DEBUG(0,("unable to generate machine SID.\n")); + return False; + } + + /* + * Do an exclusive blocking lock on the file. + */ + + if (!do_file_lock( fd, 60, F_WRLCK)) { + DEBUG(0,("unable to lock file %s. Error was %s\n", + sid_file, strerror(errno) )); + close(fd); + return False; + } + + /* + * At this point we have a blocking lock on the SID + * file - check if in the meantime someone else wrote + * SID data into the file. If so - they were here first, + * use their data. + */ + + if (sys_fstat( fd, &st) < 0) { + DEBUG(0,("unable to stat file %s. Error was %s\n", + sid_file, strerror(errno) )); + close(fd); + return False; + } + + if (st.st_size > 0) { + /* + * Unlock as soon as possible to reduce + * contention on the exclusive lock. + */ + do_file_lock( fd, 60, F_UNLCK); + + /* + * We have a valid SID - read it. + */ + + if (!read_sid_from_file( fd, sid_file)) { + DEBUG(0,("unable to read file %s. Error was %s\n", + sid_file, strerror(errno) )); + close(fd); + return False; + } + close(fd); + return True; + } + + /* + * The file is still empty and we have an exlusive lock on it. + * Write out out SID data into the file. + */ + + if (fchmod(fd, 0644) < 0) { + DEBUG(0,("unable to set correct permissions on file %s. \ +Error was %s\n", sid_file, strerror(errno) )); + close(fd); + return False; + } + + if (write( fd, sid_string, strlen(sid_string)) != strlen(sid_string)) { + DEBUG(0,("unable to write file %s. Error was %s\n", + sid_file, strerror(errno) )); + close(fd); + return False; + } + + /* + * Unlock & exit. + */ + + do_file_lock( fd, 60, F_UNLCK); + close(fd); + return True; +} + +/************************************************************************** + turns a domain name into a SID. + + *** side-effect: if the domain name is NULL, it is set to our domain *** + +***************************************************************************/ +BOOL map_domain_name_to_sid(DOM_SID *sid, char **nt_domain) +{ + int i = 0; + + if (nt_domain == NULL) + { + sid_copy(sid, &global_sam_sid); + return True; + } + + if ((*nt_domain) == NULL) + { + DEBUG(5,("map_domain_name_to_sid: overriding NULL name to %s\n", + global_sam_name)); + (*nt_domain) = strdup(global_sam_name); + sid_copy(sid, &global_sam_sid); + return True; + } + + if ((*nt_domain)[0] == 0) + { + DEBUG(5,("map_domain_name_to_sid: overriding blank name to %s\n", + global_sam_name)); + free(*nt_domain); + (*nt_domain) = strdup(global_sam_name); + sid_copy(sid, &global_sam_sid); + return True; + } + + DEBUG(5,("map_domain_name_to_sid: %s\n", (*nt_domain))); + + while (sid_name_map[i].name != NULL) + { + DEBUG(5,("compare: %s\n", sid_name_map[i].name)); + if (strequal(sid_name_map[i].name, (*nt_domain))) + { + fstring sid_str; + sid_copy(sid, sid_name_map[i].sid); + sid_to_string(sid_str, sid_name_map[i].sid); + DEBUG(5,("found %s\n", sid_str)); + return True; + } + i++; + } + + DEBUG(0,("map_domain_name_to_sid: mapping to %s NOT IMPLEMENTED\n", + (*nt_domain))); + return False; +} + +/************************************************************************** + turns a domain SID into a name. + +***************************************************************************/ +BOOL map_domain_sid_to_name(DOM_SID *sid, char *nt_domain) +{ + fstring sid_str; + int i = 0; + sid_to_string(sid_str, sid); + + DEBUG(5,("map_domain_sid_to_name: %s\n", sid_str)); + + if (nt_domain == NULL) + { + return False; + } + + while (sid_name_map[i].sid != NULL) + { + sid_to_string(sid_str, sid_name_map[i].sid); + DEBUG(5,("compare: %s\n", sid_str)); + if (sid_equal(sid_name_map[i].sid, sid)) + { + fstrcpy(nt_domain, sid_name_map[i].name); + DEBUG(5,("found %s\n", nt_domain)); + return True; + } + i++; + } + + DEBUG(0,("map_domain_sid_to_name: mapping NOT IMPLEMENTED\n")); + + return False; +} + +/************************************************************************** + splits a name of format \DOMAIN\name into its two components. + sets the DOMAIN name to global_sam_name if it has not been specified. +***************************************************************************/ +BOOL split_domain_name(char *fullname, char *domain, char *name) +{ + fstring full_name; + char *p; + + if (fullname == NULL || domain == NULL || name == NULL) + { + return False; + } + + if (fullname[0] == '\\') + { + fullname++; + } + fstrcpy(full_name, fullname); + p = strchr(full_name+1, '\\'); + + if (p != NULL) + { + *p = 0; + fstrcpy(domain, full_name); + fstrcpy(name, p+1); + } + else + { + fstrcpy(domain, global_sam_name); + fstrcpy(name, full_name); + } + + DEBUG(5,("name '%s' split into '%s\\%s'\n", fullname, domain, name)); + return True; +} |