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