"retired" two modules to preserve their cvs history.
[samba.git] / source / lib / sids.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    Samba utility functions
5    Copyright (C) Andrew Tridgell 1992-1998
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23
24
25 extern int DEBUGLEVEL;
26 extern pstring scope;
27 extern pstring global_myname;
28
29 /*
30  * This is set on startup - it defines the SID for this
31  * machine, and therefore the SAM database for which it is
32  * responsible.
33  */
34
35 DOM_SID global_sam_sid;
36
37 /*
38  * This is the name associated with the SAM database for
39  * which this machine is responsible.  In the case of a PDC
40  * or PDC, this name is the same as the workgroup.  In the
41  * case of "security = domain" mode, this is the same as
42  * the name of the server (global_myname).
43  */
44
45 fstring global_sam_name; 
46
47 /*
48  * This is obtained on startup - it defines the SID for which
49  * this machine is a member.  It is therefore only set, and
50  * used, in "security = domain" mode.
51  */
52
53 DOM_SID global_member_sid;
54
55 /*
56  * note the lack of a "global_member_name" - this is because
57  * this is the same as "global_myworkgroup".
58  */
59
60 extern fstring global_myworkgroup;
61 /* fstring global_member_dom_name; */
62
63 /*
64  * some useful sids
65  */
66
67 DOM_SID global_sid_S_1_5_20; /* local well-known domain */
68 DOM_SID global_sid_S_1_1;    /* everyone */
69 DOM_SID global_sid_S_1_3;    /* Creator Owner */
70 DOM_SID global_sid_S_1_5;    /* NT Authority */
71
72 static struct sid_name_map_info
73 {
74         DOM_SID *sid;
75         char *name;
76
77 }
78 sid_name_map[] =
79 {
80         { &global_sid_S_1_5_20, "BUILTIN" },
81         { &global_sid_S_1_1   , "Everyone" },
82         { &global_sid_S_1_3   , "Creator Owner" },
83         { &global_sid_S_1_5   , "NT Authority" },
84         { &global_sam_sid     , global_sam_name },
85         { &global_member_sid  , global_myworkgroup },
86         { NULL                , NULL      }
87 };
88
89 /****************************************************************************
90  Read the machine SID from a file.
91 ****************************************************************************/
92
93 static BOOL read_sid_from_file(int fd, char *sid_file)
94 {   
95   fstring fline;
96         fstring sid_str;
97     
98   memset(fline, '\0', sizeof(fline));
99
100   if (read(fd, fline, sizeof(fline) -1 ) < 0) {
101     DEBUG(0,("unable to read file %s. Error was %s\n",
102            sid_file, strerror(errno) ));
103     return False;
104   }
105
106   /*
107    * Convert to the machine SID.
108    */
109
110   fline[sizeof(fline)-1] = '\0';
111   if (!string_to_sid( &global_sam_sid, fline)) {
112     DEBUG(0,("unable to generate machine SID.\n"));
113     return False;
114   }
115
116         sid_to_string(sid_str, &global_sam_sid);
117         DEBUG(5,("read_sid_from_file: sid %s\n", sid_str));
118
119   return True;
120 }
121
122 /****************************************************************************
123  sets up the name associated with the SAM database for which we are responsible
124 ****************************************************************************/
125 void get_sam_domain_name(void)
126 {
127         switch (lp_server_role())
128         {
129                 case ROLE_DOMAIN_PDC:
130                 case ROLE_DOMAIN_BDC:
131                 {
132                         /* we are PDC (or BDC) for a Domain */
133                         fstrcpy(global_sam_name, lp_workgroup());
134                         break;
135                 }
136                 case ROLE_DOMAIN_MEMBER:
137                 {
138                         /* we are a "PDC", but FOR LOCAL SAM DATABASE ONLY */
139                         fstrcpy(global_sam_name, global_myname);
140                         break;
141                 }
142                 default:
143                 {
144                         /* no domain role, probably due to "security = share" */
145                         memset(global_sam_name, 0, sizeof(global_sam_name));
146                         break;
147                 }
148         }
149 }
150
151 /****************************************************************************
152  obtain the sid from the PDC.  do some verification along the way...
153 ****************************************************************************/
154 BOOL get_member_domain_sid(void)
155 {
156         POLICY_HND pol;
157         fstring srv_name;
158         struct cli_state cli;
159         BOOL res = True;
160         DOM_SID sid3;
161         DOM_SID sid5;
162         fstring dom3;
163         fstring dom5;
164
165         switch (lp_server_role())
166         {
167                 case ROLE_DOMAIN_NONE:
168                 {
169                         ZERO_STRUCT(global_member_sid);
170                         return True;
171                 }
172                 case ROLE_DOMAIN_PDC:
173                 {
174                         sid_copy(&global_member_sid, &global_sam_sid);
175                         return True;
176                 }
177                 default:
178                 {
179                         /* member or BDC, we're going for connection to PDC */
180                         break;
181                 }
182         }
183
184         if (!cli_connect_serverlist(&cli, lp_passwordserver()))
185         {
186                 DEBUG(0,("get_member_domain_sid: unable to initialise client connection.\n"));
187                 return False;
188         }
189
190         /*
191          * Ok - we have an anonymous connection to the IPC$ share.
192          * Now start the NT Domain stuff :-).
193          */
194
195         fstrcpy(dom3, "");
196         fstrcpy(dom5, "");
197         ZERO_STRUCT(sid3);
198         ZERO_STRUCT(sid5);
199
200         fstrcpy(srv_name, "\\\\");
201         fstrcat(srv_name, global_myname);
202         strupper(srv_name);
203
204         /* open LSARPC session. */
205         res = res ? cli_nt_session_open(&cli, PIPE_LSARPC) : False;
206
207         /* lookup domain controller; receive a policy handle */
208         res = res ? do_lsa_open_policy(&cli, srv_name, &pol, False) : False;
209
210         /* send client info query, level 3.  receive domain name and sid */
211         res = res ? do_lsa_query_info_pol(&cli, &pol, 3, dom3, &sid3) : False;
212
213         /* send client info query, level 5.  receive domain name and sid */
214         res = res ? do_lsa_query_info_pol(&cli, &pol, 5, dom5, &sid5) : False;
215
216         /* close policy handle */
217         res = res ? do_lsa_close(&cli, &pol) : False;
218
219         /* close the session */
220         cli_nt_session_close(&cli);
221         cli_ulogoff(&cli);
222         cli_shutdown(&cli);
223
224         if (res)
225         {
226                 pstring sid;
227                 DEBUG(2,("LSA Query Info Policy\n"));
228                 sid_to_string(sid, &sid3);
229                 DEBUG(2,("Domain Member     - Domain: %s SID: %s\n", dom3, sid));
230                 sid_to_string(sid, &sid5);
231                 DEBUG(2,("Domain Controller - Domain: %s SID: %s\n", dom5, sid));
232
233                 if (!strequal(dom3, global_myworkgroup) ||
234                     !strequal(dom5, global_myworkgroup))
235                 {
236                         DEBUG(0,("get_member_domain_sid: %s is a DC for %s not %s\n",
237                                   cli.desthost, dom5, global_myworkgroup));
238                         res = False;
239                 }
240         }
241         else
242         {
243                 DEBUG(1,("lsa query info failed\n"));
244         }
245
246         if (!res)
247         {
248                 DEBUG(0,("get_member_domain_sid: unable to obtain Domain member SID\n"));
249         }
250         else
251         {
252                 /* this is a _lot_ of trouble to go to for just this info: */
253                 global_member_sid = sid5;
254         }
255
256         return res;
257 }
258
259 /****************************************************************************
260  creates some useful well known sids
261 ****************************************************************************/
262 void generate_wellknown_sids(void)
263 {
264         string_to_sid(&global_sid_S_1_5_20, "S-1-5-32");
265         string_to_sid(&global_sid_S_1_1   , "S-1-1"   );
266         string_to_sid(&global_sid_S_1_3   , "S-1-3"   );
267         string_to_sid(&global_sid_S_1_5   , "S-1-5"   );
268 }
269
270 /****************************************************************************
271  Generate the global machine sid. Look for the MACHINE.SID file first, if
272  not found then look in smb.conf and use it to create the MACHINE.SID file.
273 ****************************************************************************/
274 BOOL generate_sam_sid(void)
275 {
276         int fd;
277         int i;
278         char *p;
279         pstring sid_file;
280         fstring sid_string;
281         SMB_STRUCT_STAT st;
282         uchar raw_sid_data[12];
283
284         pstrcpy(sid_file, lp_smb_passwd_file());
285         p = strrchr(sid_file, '/');
286         if (p != NULL) {
287                 *++p = '\0';
288         }
289
290         if (!directory_exist(sid_file, NULL)) {
291                 if (dos_mkdir(sid_file, 0700) != 0) {
292                         DEBUG(0,("can't create private directory %s : %s\n",
293                                  sid_file, strerror(errno)));
294                         return False;
295                 }
296         }
297
298         pstrcat(sid_file, "MACHINE.SID");
299     
300         if ((fd = sys_open(sid_file, O_RDWR | O_CREAT, 0644)) == -1) {
301                 DEBUG(0,("unable to open or create file %s. Error was %s\n",
302                          sid_file, strerror(errno) ));
303                 return False;
304         } 
305   
306         /*
307          * Check if the file contains data.
308          */
309         
310         if (sys_fstat( fd, &st) < 0) {
311                 DEBUG(0,("unable to stat file %s. Error was %s\n",
312                          sid_file, strerror(errno) ));
313                 close(fd);
314                 return False;
315         } 
316   
317         if (st.st_size > 0) {
318                 /*
319                  * We have a valid SID - read it.
320                  */
321                 if (!read_sid_from_file( fd, sid_file)) {
322                         DEBUG(0,("unable to read file %s. Error was %s\n",
323                                  sid_file, strerror(errno) ));
324                         close(fd);
325                         return False;
326                 }
327                 close(fd);
328                 return True;
329         } 
330   
331         /*
332          * Generate the new sid data & turn it into a string.
333          */
334         generate_random_buffer( raw_sid_data, 12, True);
335                 
336         fstrcpy( sid_string, "S-1-5-21");
337         for( i = 0; i < 3; i++) {
338                 fstring tmp_string;
339                 slprintf( tmp_string, sizeof(tmp_string) - 1, "-%u", IVAL(raw_sid_data, i*4));
340                 fstrcat( sid_string, tmp_string);
341         }
342         
343         fstrcat(sid_string, "\n");
344         
345         /*
346          * Ensure our new SID is valid.
347          */
348         
349         if (!string_to_sid( &global_sam_sid, sid_string)) {
350                 DEBUG(0,("unable to generate machine SID.\n"));
351                 return False;
352         } 
353   
354         /*
355          * Do an exclusive blocking lock on the file.
356          */
357         
358         if (!do_file_lock( fd, 60, F_WRLCK)) {
359                 DEBUG(0,("unable to lock file %s. Error was %s\n",
360                          sid_file, strerror(errno) ));
361                 close(fd);
362                 return False;
363         } 
364   
365         /*
366          * At this point we have a blocking lock on the SID
367          * file - check if in the meantime someone else wrote
368          * SID data into the file. If so - they were here first,
369          * use their data.
370          */
371         
372         if (sys_fstat( fd, &st) < 0) {
373                 DEBUG(0,("unable to stat file %s. Error was %s\n",
374                          sid_file, strerror(errno) ));
375                 close(fd);
376                 return False;
377         } 
378   
379         if (st.st_size > 0) {
380                 /*
381                  * Unlock as soon as possible to reduce
382                  * contention on the exclusive lock.
383                  */ 
384                 do_file_lock( fd, 60, F_UNLCK);
385                 
386                 /*
387                  * We have a valid SID - read it.
388                  */
389                 
390                 if (!read_sid_from_file( fd, sid_file)) {
391                         DEBUG(0,("unable to read file %s. Error was %s\n",
392                                  sid_file, strerror(errno) ));
393                         close(fd);
394                         return False;
395                 }
396                 close(fd);
397                 return True;
398         } 
399         
400         /*
401          * The file is still empty and we have an exlusive lock on it.
402          * Write out out SID data into the file.
403          */
404         
405         if (fchmod(fd, 0644) < 0) {
406                 DEBUG(0,("unable to set correct permissions on file %s. \
407 Error was %s\n", sid_file, strerror(errno) ));
408                 close(fd);
409                 return False;
410         } 
411         
412         if (write( fd, sid_string, strlen(sid_string)) != strlen(sid_string)) {
413                 DEBUG(0,("unable to write file %s. Error was %s\n",
414                          sid_file, strerror(errno) ));
415                 close(fd);
416                 return False;
417         } 
418         
419         /*
420          * Unlock & exit.
421          */
422         
423         do_file_lock( fd, 60, F_UNLCK);
424         close(fd);
425         return True;
426 }   
427
428 /**************************************************************************
429  turns a domain name into a SID.
430
431  *** side-effect: if the domain name is NULL, it is set to our domain ***
432
433 ***************************************************************************/
434 BOOL map_domain_name_to_sid(DOM_SID *sid, char **nt_domain)
435 {
436         int i = 0;
437
438         if (nt_domain == NULL)
439         {
440                 sid_copy(sid, &global_sam_sid);
441                 return True;
442         }
443
444         if ((*nt_domain) == NULL)
445         {
446                 DEBUG(5,("map_domain_name_to_sid: overriding NULL name to %s\n",
447                           global_sam_name));
448                 (*nt_domain) = strdup(global_sam_name);
449                 sid_copy(sid, &global_sam_sid);
450                 return True;
451         }
452
453         if ((*nt_domain)[0] == 0)
454         {
455                 DEBUG(5,("map_domain_name_to_sid: overriding blank name to %s\n",
456                           global_sam_name));
457                 free(*nt_domain);
458                 (*nt_domain) = strdup(global_sam_name);
459                 sid_copy(sid, &global_sam_sid);
460                 return True;
461         }
462
463         DEBUG(5,("map_domain_name_to_sid: %s\n", (*nt_domain)));
464
465         while (sid_name_map[i].name != NULL)
466         {
467                 DEBUG(5,("compare: %s\n", sid_name_map[i].name));
468                 if (strequal(sid_name_map[i].name, (*nt_domain)))
469                 {
470                         fstring sid_str;
471                         sid_copy(sid, sid_name_map[i].sid);
472                         sid_to_string(sid_str, sid_name_map[i].sid);
473                         DEBUG(5,("found %s\n", sid_str));
474                         return True;
475                 }
476                 i++;
477         }
478
479         DEBUG(0,("map_domain_name_to_sid: mapping to %s NOT IMPLEMENTED\n",
480                   (*nt_domain)));
481         return False;
482 }
483
484 /**************************************************************************
485  turns a domain SID into a name.
486
487 ***************************************************************************/
488 BOOL map_domain_sid_to_name(DOM_SID *sid, char *nt_domain)
489 {
490         fstring sid_str;
491         int i = 0;
492         sid_to_string(sid_str, sid);
493
494         DEBUG(5,("map_domain_sid_to_name: %s\n", sid_str));
495
496         if (nt_domain == NULL)
497         {
498                 return False;
499         }
500
501         while (sid_name_map[i].sid != NULL)
502         {
503                 sid_to_string(sid_str, sid_name_map[i].sid);
504                 DEBUG(5,("compare: %s\n", sid_str));
505                 if (sid_equal(sid_name_map[i].sid, sid))
506                 {
507                         fstrcpy(nt_domain, sid_name_map[i].name);
508                         DEBUG(5,("found %s\n", nt_domain));
509                         return True;
510                 }
511                 i++;
512         }
513
514         DEBUG(0,("map_domain_sid_to_name: mapping NOT IMPLEMENTED\n"));
515
516         return False;
517 }
518
519 /**************************************************************************
520  splits a name of format \DOMAIN\name into its two components.
521  sets the DOMAIN name to global_sam_name if it has not been specified.
522 ***************************************************************************/
523 BOOL split_domain_name(char *fullname, char *domain, char *name)
524 {
525         fstring full_name;
526         char *p;
527
528         if (fullname == NULL || domain == NULL || name == NULL)
529         {
530                 return False;
531         }
532
533         if (fullname[0] == '\\')
534         {
535                 fullname++;
536         }
537         fstrcpy(full_name, fullname);
538         p = strchr(full_name+1, '\\');
539
540         if (p != NULL)
541         {
542                 *p = 0;
543                 fstrcpy(domain, full_name);
544                 fstrcpy(name, p+1);
545         }
546         else
547         {
548                 fstrcpy(domain, global_sam_name);
549                 fstrcpy(name, full_name);
550         }
551
552         DEBUG(5,("name '%s' split into '%s\\%s'\n", fullname, domain, name));
553         return True;
554 }