Updates to the 'name -> sid' code:
[tprouty/samba.git] / source / smbd / uid.c
1 /* 
2    Unix SMB/CIFS implementation.
3    uid/user handling
4    Copyright (C) Andrew Tridgell 1992-1998
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "includes.h"
22
23 /* what user is current? */
24 extern struct current_user current_user;
25
26 /****************************************************************************
27  Become the guest user without changing the security context stack.
28 ****************************************************************************/
29
30 BOOL change_to_guest(void)
31 {
32         static struct passwd *pass=NULL;
33
34         if (!pass) {
35                 /* Don't need to free() this as its stored in a static */
36                 pass = getpwnam_alloc(lp_guestaccount());
37                 if (!pass)
38                         return(False);
39         }
40         
41 #ifdef AIX
42         /* MWW: From AIX FAQ patch to WU-ftpd: call initgroups before 
43            setting IDs */
44         initgroups(pass->pw_name, pass->pw_gid);
45 #endif
46         
47         set_sec_ctx(pass->pw_uid, pass->pw_gid, 0, NULL, NULL);
48         
49         current_user.conn = NULL;
50         current_user.vuid = UID_FIELD_INVALID;
51         
52         return True;
53 }
54
55 /*******************************************************************
56  Check if a username is OK.
57 ********************************************************************/
58
59 static BOOL check_user_ok(connection_struct *conn, user_struct *vuser,int snum)
60 {
61         int i;
62         for (i=0;i<conn->uid_cache.entries;i++)
63                 if (conn->uid_cache.list[i] == vuser->uid)
64                         return(True);
65
66         if (!user_ok(vuser->user.unix_name,snum))
67                 return(False);
68
69         i = conn->uid_cache.entries % UID_CACHE_SIZE;
70         conn->uid_cache.list[i] = vuser->uid;
71
72         if (conn->uid_cache.entries < UID_CACHE_SIZE)
73                 conn->uid_cache.entries++;
74
75         return(True);
76 }
77
78 /****************************************************************************
79  Become the user of a connection number without changing the security context
80  stack, but modify the currnet_user entries.
81 ****************************************************************************/
82
83 BOOL change_to_user(connection_struct *conn, uint16 vuid)
84 {
85         user_struct *vuser = get_valid_user_struct(vuid);
86         int snum;
87         gid_t gid;
88         uid_t uid;
89         char group_c;
90         BOOL must_free_token = False;
91         NT_USER_TOKEN *token = NULL;
92
93         if (!conn) {
94                 DEBUG(2,("change_to_user: Connection not open\n"));
95                 return(False);
96         }
97
98         /*
99          * We need a separate check in security=share mode due to vuid
100          * always being UID_FIELD_INVALID. If we don't do this then
101          * in share mode security we are *always* changing uid's between
102          * SMB's - this hurts performance - Badly.
103          */
104
105         if((lp_security() == SEC_SHARE) && (current_user.conn == conn) &&
106            (current_user.uid == conn->uid)) {
107                 DEBUG(4,("change_to_user: Skipping user change - already user\n"));
108                 return(True);
109         } else if ((current_user.conn == conn) && 
110                    (vuser != 0) && (current_user.vuid == vuid) && 
111                    (current_user.uid == vuser->uid)) {
112                 DEBUG(4,("change_to_user: Skipping user change - already user\n"));
113                 return(True);
114         }
115
116         snum = SNUM(conn);
117
118         if((vuser != NULL) && !check_user_ok(conn, vuser, snum))
119                 return False;
120
121         if (conn->force_user || 
122                 conn->admin_user ||
123             (lp_security() == SEC_SHARE)) {
124                 uid = conn->uid;
125                 gid = conn->gid;
126                 current_user.groups = conn->groups;
127                 current_user.ngroups = conn->ngroups;
128                 token = conn->nt_user_token;
129         } else {
130                 if (!vuser) {
131                         DEBUG(2,("change_to_user: Invalid vuid used %d\n",vuid));
132                         return(False);
133                 }
134                 uid = vuser->uid;
135                 gid = vuser->gid;
136                 current_user.ngroups = vuser->n_groups;
137                 current_user.groups  = vuser->groups;
138                 token = vuser->nt_user_token;
139         }
140
141         /*
142          * See if we should force group for this service.
143          * If so this overrides any group set in the force
144          * user code.
145          */
146
147         if((group_c = *lp_force_group(snum))) {
148                 BOOL is_guest = False;
149
150                 if(group_c == '+') {
151
152                         /*
153                          * Only force group if the user is a member of
154                          * the service group. Check the group memberships for
155                          * this user (we already have this) to
156                          * see if we should force the group.
157                          */
158
159                         int i;
160                         for (i = 0; i < current_user.ngroups; i++) {
161                                 if (current_user.groups[i] == conn->gid) {
162                                         gid = conn->gid;
163                                         break;
164                                 }
165                         }
166                 } else {
167                         gid = conn->gid;
168                 }
169
170                 /*
171                  * We've changed the group list in the token - we must
172                  * re-create it.
173                  */
174
175                 if (vuser && vuser->guest)
176                         is_guest = True;
177
178                 token = create_nt_token(uid, gid, current_user.ngroups, current_user.groups, is_guest, NULL);
179                 must_free_token = True;
180         }
181         
182         set_sec_ctx(uid, gid, current_user.ngroups, current_user.groups, token);
183
184         /*
185          * Free the new token (as set_sec_ctx copies it).
186          */
187
188         if (must_free_token)
189                 delete_nt_token(&token);
190
191         current_user.conn = conn;
192         current_user.vuid = vuid;
193
194         DEBUG(5,("change_to_user uid=(%d,%d) gid=(%d,%d)\n",
195                  (int)getuid(),(int)geteuid(),(int)getgid(),(int)getegid()));
196   
197         return(True);
198 }
199
200 /****************************************************************************
201  Go back to being root without changing the security context stack,
202  but modify the current_user entries.
203 ****************************************************************************/
204
205 BOOL change_to_root_user(void)
206 {
207         set_root_sec_ctx();
208
209         DEBUG(5,("change_to_root_user: now uid=(%d,%d) gid=(%d,%d)\n",
210                 (int)getuid(),(int)geteuid(),(int)getgid(),(int)getegid()));
211
212         current_user.conn = NULL;
213         current_user.vuid = UID_FIELD_INVALID;
214
215         return(True);
216 }
217
218 /****************************************************************************
219  Become the user of an authenticated connected named pipe.
220  When this is called we are currently running as the connection
221  user. Doesn't modify current_user.
222 ****************************************************************************/
223
224 BOOL become_authenticated_pipe_user(pipes_struct *p)
225 {
226         if (!push_sec_ctx())
227                 return False;
228
229         set_sec_ctx(p->pipe_user.uid, p->pipe_user.gid, 
230                     p->pipe_user.ngroups, p->pipe_user.groups, p->pipe_user.nt_user_token);
231
232         return True;
233 }
234
235 /****************************************************************************
236  Unbecome the user of an authenticated connected named pipe.
237  When this is called we are running as the authenticated pipe
238  user and need to go back to being the connection user. Doesn't modify
239  current_user.
240 ****************************************************************************/
241
242 BOOL unbecome_authenticated_pipe_user(void)
243 {
244         return pop_sec_ctx();
245 }
246
247 /****************************************************************************
248  Utility functions used by become_xxx/unbecome_xxx.
249 ****************************************************************************/
250
251 struct conn_ctx {
252         connection_struct *conn;
253         uint16 vuid;
254 };
255  
256 /* A stack of current_user connection contexts. */
257  
258 static struct conn_ctx conn_ctx_stack[MAX_SEC_CTX_DEPTH];
259 static int conn_ctx_stack_ndx;
260
261 static void push_conn_ctx(void)
262 {
263         struct conn_ctx *ctx_p;
264  
265         /* Check we don't overflow our stack */
266  
267         if (conn_ctx_stack_ndx == MAX_SEC_CTX_DEPTH) {
268                 DEBUG(0, ("Connection context stack overflow!\n"));
269                 smb_panic("Connection context stack overflow!\n");
270         }
271  
272         /* Store previous user context */
273         ctx_p = &conn_ctx_stack[conn_ctx_stack_ndx];
274  
275         ctx_p->conn = current_user.conn;
276         ctx_p->vuid = current_user.vuid;
277  
278         DEBUG(3, ("push_conn_ctx(%u) : conn_ctx_stack_ndx = %d\n",
279                 (unsigned int)ctx_p->vuid, conn_ctx_stack_ndx ));
280
281         conn_ctx_stack_ndx++;
282 }
283
284 static void pop_conn_ctx(void)
285 {
286         struct conn_ctx *ctx_p;
287  
288         /* Check for stack underflow. */
289
290         if (conn_ctx_stack_ndx == 0) {
291                 DEBUG(0, ("Connection context stack underflow!\n"));
292                 smb_panic("Connection context stack underflow!\n");
293         }
294
295         conn_ctx_stack_ndx--;
296         ctx_p = &conn_ctx_stack[conn_ctx_stack_ndx];
297
298         current_user.conn = ctx_p->conn;
299         current_user.vuid = ctx_p->vuid;
300
301         ctx_p->conn = NULL;
302         ctx_p->vuid = UID_FIELD_INVALID;
303 }
304
305 void init_conn_ctx(void)
306 {
307     int i;
308  
309     /* Initialise connection context stack */
310         for (i = 0; i < MAX_SEC_CTX_DEPTH; i++) {
311                 conn_ctx_stack[i].conn = NULL;
312                 conn_ctx_stack[i].vuid = UID_FIELD_INVALID;
313     }
314 }
315
316 /****************************************************************************
317  Temporarily become a root user.  Must match with unbecome_root(). Saves and
318  restores the connection context.
319 ****************************************************************************/
320
321 void become_root(void)
322 {
323         push_sec_ctx();
324         push_conn_ctx();
325         set_root_sec_ctx();
326 }
327
328 /* Unbecome the root user */
329
330 void unbecome_root(void)
331 {
332         pop_sec_ctx();
333         pop_conn_ctx();
334 }
335
336 /****************************************************************************
337  Push the current security context then force a change via change_to_user().
338  Saves and restores the connection context.
339 ****************************************************************************/
340
341 BOOL become_user(connection_struct *conn, uint16 vuid)
342 {
343         if (!push_sec_ctx())
344                 return False;
345
346         push_conn_ctx();
347
348         if (!change_to_user(conn, vuid)) {
349                 pop_sec_ctx();
350                 pop_conn_ctx();
351                 return False;
352         }
353
354         return True;
355 }
356
357 BOOL unbecome_user(void)
358 {
359         pop_sec_ctx();
360         pop_conn_ctx();
361         return True;
362 }
363
364 /*****************************************************************
365  Convert the suplimentary SIDs returned in a netlogon into UNIX
366  group gid_t's. Add to the total group array.
367 *****************************************************************/
368  
369 void add_supplementary_nt_login_groups(int *n_groups, gid_t **pp_groups, NT_USER_TOKEN **pptok)
370 {
371         int total_groups;
372         int current_n_groups = *n_groups;
373         gid_t *final_groups = NULL;
374         size_t i;
375         NT_USER_TOKEN *ptok = *pptok;
376         NT_USER_TOKEN *new_tok = NULL;
377  
378         if (!ptok || (ptok->num_sids == 0))
379                 return;
380
381         new_tok = dup_nt_token(ptok);
382         if (!new_tok) {
383                 DEBUG(0,("add_supplementary_nt_login_groups: Failed to malloc new token\n"));
384                 return;
385         }
386         /* Leave the allocated space but empty the number of SIDs. */
387         new_tok->num_sids = 0;
388
389         total_groups = current_n_groups + ptok->num_sids;
390  
391         final_groups = (gid_t *)malloc(total_groups * sizeof(gid_t));
392         if (!final_groups) {
393                 DEBUG(0,("add_supplementary_nt_login_groups: Failed to malloc new groups.\n"));
394                 delete_nt_token(&new_tok);
395                 return;
396         }
397  
398         memcpy(final_groups, *pp_groups, current_n_groups * sizeof(gid_t));
399         for (i = 0; i < ptok->num_sids; i++) {
400                 enum SID_NAME_USE sid_type;
401                 gid_t new_grp;
402  
403                 if (sid_to_gid(&ptok->user_sids[i], &new_grp, &sid_type)) {
404                         /*
405                          * Don't add the gid_t if it is already in the current group
406                          * list. Some UNIXen don't like the same group more than once.
407                          */
408                         int j;
409
410                         for (j = 0; j < current_n_groups; j++)
411                                 if (final_groups[j] == new_grp)
412                                         break;
413                 
414                         if ( j == current_n_groups) {
415                                 /* Group not already present. */
416                                 final_groups[current_n_groups++] = new_grp;
417                         }
418                 } else {
419                         /* SID didn't map. Copy to the new token to be saved. */
420                         sid_copy(&new_tok->user_sids[new_tok->num_sids++], &ptok->user_sids[i]);
421                 }
422         }
423  
424         SAFE_FREE(*pp_groups);
425         *pp_groups = final_groups;
426         *n_groups = current_n_groups;
427
428         /* Replace the old token with the truncated one. */
429         delete_nt_token(&ptok);
430         *pptok = new_tok;
431 }
432
433 /*****************************************************************
434  *THE CANONICAL* convert name to SID function.
435  Tries local lookup first - for local domains - then uses winbind.
436 *****************************************************************/  
437
438 BOOL lookup_name(const char *domain, const char *name, DOM_SID *psid, enum SID_NAME_USE *name_type)
439 {
440         extern pstring global_myname;
441         extern fstring global_myworkgroup;
442         fstring sid;
443         BOOL ret = False;
444         
445         *name_type = SID_NAME_UNKNOWN;
446
447         /* If we are looking up a domain user, make sure it is
448            for the local machine only */
449         
450         switch (lp_server_role()) {
451         case ROLE_DOMAIN_PDC:
452         case ROLE_DOMAIN_BDC:
453                 if (strequal(domain, global_myworkgroup)) {
454                         ret = local_lookup_name(name, psid, name_type);
455                 }
456                 /* No break is deliberate here. JRA. */
457         default:
458                 if (ret) {
459                 } else if (strequal(global_myname, domain)) {
460                         ret = local_lookup_name(name, psid, name_type);
461                 } else {
462                         DEBUG(5, ("lookup_name: domain %s is not local\n", domain));
463                 }
464         }
465         
466         if (ret) {
467                 DEBUG(10,
468                       ("lookup_name: (local) [%s]\\[%s] -> SID %s (type %s: %u)\n",
469                        domain, name, sid_to_string(sid,psid),
470                        sid_type_lookup(*name_type), (unsigned int)*name_type));
471                 return True;
472         } else if (winbind_lookup_name(domain, name, psid, name_type)) {
473                 
474                 DEBUG(10,("lookup_name (winbindd): [%s]\\[%s] -> SID %s (type %u)\n",
475                           domain, name, sid_to_string(sid, psid), 
476                           (unsigned int)*name_type));
477                 return True;
478         }
479
480         DEBUG(10, ("lookup_name: winbind and local lookups for [%s]\\[%s] failed\n", domain, name));
481
482         return False;
483 }
484
485 /*****************************************************************
486  *THE CANONICAL* convert SID to name function.
487  Tries local lookup first - for local sids, then tries winbind.
488 *****************************************************************/  
489
490 BOOL lookup_sid(DOM_SID *sid, fstring dom_name, fstring name, enum SID_NAME_USE *name_type)
491 {
492         if (!name_type)
493                 return False;
494
495         *name_type = SID_NAME_UNKNOWN;
496
497         /* Check if this is our own sid.  This should perhaps be done by
498            winbind?  For the moment handle it here. */
499
500         if (sid->num_auths == 5) {
501                 DOM_SID tmp_sid;
502                 uint32 rid;
503
504                 sid_copy(&tmp_sid, sid);
505                 sid_split_rid(&tmp_sid, &rid);
506
507                 if (sid_equal(get_global_sam_sid(), &tmp_sid)) {
508
509                         return map_domain_sid_to_name(&tmp_sid, dom_name) &&
510                                 local_lookup_sid(sid, name, name_type);
511                 }
512         }
513
514         if (!winbind_lookup_sid(sid, dom_name, name, name_type)) {
515                 fstring sid_str;
516                 DOM_SID tmp_sid;
517                 uint32 rid;
518
519                 DEBUG(10,("lookup_sid: winbind lookup for SID %s failed - trying local.\n", sid_to_string(sid_str, sid) ));
520
521                 sid_copy(&tmp_sid, sid);
522                 sid_split_rid(&tmp_sid, &rid);
523                 return map_domain_sid_to_name(&tmp_sid, dom_name) &&
524                         lookup_known_rid(&tmp_sid, rid, name, name_type);
525         }
526         return True;
527 }
528
529 /*****************************************************************
530  *THE CANONICAL* convert uid_t to SID function.
531  Tries winbind first - then uses local lookup.
532  Returns SID pointer.
533 *****************************************************************/  
534
535 DOM_SID *uid_to_sid(DOM_SID *psid, uid_t uid)
536 {
537         uid_t low, high;
538         fstring sid;
539
540         if (lp_winbind_uid(&low, &high) && uid >= low && uid <= high) {
541                 if (winbind_uid_to_sid(psid, uid)) {
542
543                         DEBUG(10,("uid_to_sid: winbindd %u -> %s\n",
544                                 (unsigned int)uid, sid_to_string(sid, psid)));
545
546                         return psid;
547                 }
548         }
549         
550         /* Make sure we report failure, (when psid == NULL) */
551         become_root();
552         psid = local_uid_to_sid(psid, uid);
553         unbecome_root();
554
555         DEBUG(10,("uid_to_sid: local %u -> %s\n", (unsigned int)uid, sid_to_string(sid, psid)));
556
557         return psid;
558 }
559
560 /*****************************************************************
561  *THE CANONICAL* convert gid_t to SID function.
562  Tries winbind first - then uses local lookup.
563  Returns SID pointer.
564 *****************************************************************/  
565
566 DOM_SID *gid_to_sid(DOM_SID *psid, gid_t gid)
567 {
568         gid_t low, high;
569         fstring sid;
570
571         if (lp_winbind_gid(&low, &high) && gid >= low && gid <= high) {
572                 if (winbind_gid_to_sid(psid, gid)) {
573
574                         DEBUG(10,("gid_to_sid: winbindd %u -> %s\n",
575                                 (unsigned int)gid, sid_to_string(sid, psid)));
576                         
577                         return psid;
578                 }
579         }
580
581         /* Make sure we report failure, (when psid == NULL) */
582         psid = local_gid_to_sid(psid, gid);
583         
584         DEBUG(10,("gid_to_sid: local %u -> %s\n", (unsigned int)gid, sid_to_string(sid, psid)));
585
586         return psid;
587 }
588
589 /*****************************************************************
590  *THE CANONICAL* convert SID to uid function.
591  Tries winbind first - then uses local lookup.
592  Returns True if this name is a user sid and the conversion
593  was done correctly, False if not. sidtype is set by this function.
594 *****************************************************************/  
595
596 BOOL sid_to_uid(DOM_SID *psid, uid_t *puid, enum SID_NAME_USE *sidtype)
597 {
598         fstring sid_str;
599
600         /* if we know its local then don't try winbindd */
601         if (sid_compare_domain(get_global_sam_sid(), psid) == 0) {
602                 return local_sid_to_uid(puid, psid, sidtype);
603         }
604
605 /* (tridge) I commented out the slab of code below in order to support foreign SIDs
606    Do we really need to validate the type of SID we have in this case? 
607 */
608 #if 0
609         fstring dom_name, name;
610         enum SID_NAME_USE name_type;
611
612         *sidtype = SID_NAME_UNKNOWN;
613         /*
614          * First we must look up the name and decide if this is a user sid.
615          */
616
617         if ( (!winbind_lookup_sid(psid, dom_name, name, &name_type)) || (name_type != SID_NAME_USER) ) {
618                 BOOL result;
619                 DEBUG(10,("sid_to_uid: winbind lookup for sid %s failed - trying local.\n",
620                                 sid_to_string(sid_str, psid) ));
621
622                 become_root();
623                 result = local_sid_to_uid(puid, psid, sidtype);
624                 unbecome_root();
625                 return result;
626         }
627
628         /*
629          * Ensure this is a user sid.
630          */
631
632         if (name_type != SID_NAME_USER) {
633                 DEBUG(10,("sid_to_uid: winbind lookup succeeded but SID is not a uid (%u)\n",
634                                 (unsigned int)name_type ));
635                 return False;
636         }
637 #endif
638         *sidtype = SID_NAME_USER;
639
640         /*
641          * Get the uid for this SID.
642          */
643
644         if (!winbind_sid_to_uid(puid, psid)) {
645                 BOOL result;
646                 DEBUG(10,("sid_to_uid: winbind lookup for sid %s failed.\n",
647                                 sid_to_string(sid_str, psid) ));
648                 become_root();
649                 result = local_sid_to_uid(puid, psid, sidtype);
650                 unbecome_root();
651                 return result;
652         }
653
654         DEBUG(10,("sid_to_uid: winbindd %s -> %u\n",
655                 sid_to_string(sid_str, psid),
656                 (unsigned int)*puid ));
657
658         return True;
659 }
660
661 /*****************************************************************
662  *THE CANONICAL* convert SID to gid function.
663  Tries winbind first - then uses local lookup.
664  Returns True if this name is a user sid and the conversion
665  was done correctly, False if not.
666 *****************************************************************/  
667
668 BOOL sid_to_gid(DOM_SID *psid, gid_t *pgid, enum SID_NAME_USE *sidtype)
669 {
670         fstring dom_name, name, sid_str;
671         enum SID_NAME_USE name_type;
672
673         *sidtype = SID_NAME_UNKNOWN;
674
675         /*
676          * First we must look up the name and decide if this is a group sid.
677          */
678
679         if (!winbind_lookup_sid(psid, dom_name, name, &name_type)) {
680                 DEBUG(10,("sid_to_gid: winbind lookup for sid %s failed - trying local.\n",
681                                 sid_to_string(sid_str, psid) ));
682                 if (!local_sid_to_gid(pgid, psid, sidtype)) {
683                         /* this was probably a foreign sid - assume its a group rid 
684                            and continue */
685                         name_type = SID_NAME_DOM_GRP;
686                 } else {
687                         return True;
688                 }
689         }
690
691         /*
692          * Ensure this is a group sid.
693          */
694
695         if ((name_type != SID_NAME_DOM_GRP) && (name_type != SID_NAME_ALIAS) && (name_type != SID_NAME_WKN_GRP)) {
696                 DEBUG(10,("sid_to_gid: winbind lookup succeeded but SID is not a known group (%u)\n",
697                                 (unsigned int)name_type ));
698
699                 return local_sid_to_gid(pgid, psid, sidtype);
700         }
701
702         *sidtype = name_type;
703
704         /*
705          * Get the gid for this SID.
706          */
707
708         if (!winbind_sid_to_gid(pgid, psid)) {
709                 DEBUG(10,("sid_to_gid: winbind lookup for sid %s failed.\n",
710                                 sid_to_string(sid_str, psid) ));
711                 return False;
712         }
713
714         DEBUG(10,("sid_to_gid: winbindd %s -> %u\n",
715                 sid_to_string(sid_str, psid),
716                 (unsigned int)*pgid ));
717
718         return True;
719 }
720