*id_to_*id call reshape to return NTSTATUS errors
[ira/wip.git] / source3 / 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         passwd_free(&pass);
53
54         return True;
55 }
56
57 /*******************************************************************
58  Check if a username is OK.
59 ********************************************************************/
60
61 static BOOL check_user_ok(connection_struct *conn, user_struct *vuser,int snum)
62 {
63         unsigned i;
64         for (i=0;i<conn->vuid_cache.entries && i< VUID_CACHE_SIZE;i++)
65                 if (conn->vuid_cache.list[i] == vuser->vuid)
66                         return(True);
67
68         if ((conn->force_user || conn->force_group) 
69             && (conn->vuid != vuser->vuid)) {
70                 return False;
71         }
72         
73         if (!user_ok(vuser->user.unix_name,snum, vuser->groups, vuser->n_groups))
74                 return(False);
75
76         if (!share_access_check(conn, snum, vuser, conn->read_only ? FILE_READ_DATA : FILE_WRITE_DATA)) {
77                 return False;
78         }
79
80         i = conn->vuid_cache.entries % VUID_CACHE_SIZE;
81         conn->vuid_cache.list[i] = vuser->vuid;
82
83         conn->vuid_cache.entries++;
84
85         return(True);
86 }
87
88 /****************************************************************************
89  Become the user of a connection number without changing the security context
90  stack, but modify the currnet_user entries.
91 ****************************************************************************/
92
93 BOOL change_to_user(connection_struct *conn, uint16 vuid)
94 {
95         user_struct *vuser = get_valid_user_struct(vuid);
96         int snum;
97         gid_t gid;
98         uid_t uid;
99         char group_c;
100         BOOL must_free_token = False;
101         NT_USER_TOKEN *token = NULL;
102
103         if (!conn) {
104                 DEBUG(2,("change_to_user: Connection not open\n"));
105                 return(False);
106         }
107
108         /*
109          * We need a separate check in security=share mode due to vuid
110          * always being UID_FIELD_INVALID. If we don't do this then
111          * in share mode security we are *always* changing uid's between
112          * SMB's - this hurts performance - Badly.
113          */
114
115         if((lp_security() == SEC_SHARE) && (current_user.conn == conn) &&
116            (current_user.uid == conn->uid)) {
117                 DEBUG(4,("change_to_user: Skipping user change - already user\n"));
118                 return(True);
119         } else if ((current_user.conn == conn) && 
120                    (vuser != 0) && (current_user.vuid == vuid) && 
121                    (current_user.uid == vuser->uid)) {
122                 DEBUG(4,("change_to_user: Skipping user change - already user\n"));
123                 return(True);
124         }
125
126         snum = SNUM(conn);
127
128         if (conn->force_user) /* security = share sets this too */ {
129                 uid = conn->uid;
130                 gid = conn->gid;
131                 current_user.groups = conn->groups;
132                 current_user.ngroups = conn->ngroups;
133                 token = conn->nt_user_token;
134         } else if ((vuser) && check_user_ok(conn, vuser, snum)) {
135                 uid = vuser->uid;
136                 gid = vuser->gid;
137                 current_user.ngroups = vuser->n_groups;
138                 current_user.groups  = vuser->groups;
139                 token = vuser->nt_user_token;
140         } else {
141                 DEBUG(2,("change_to_user: Invalid vuid used %d or vuid not permitted access to share.\n",vuid));
142                 return False;
143         }
144
145         /*
146          * See if we should force group for this service.
147          * If so this overrides any group set in the force
148          * user code.
149          */
150
151         if((group_c = *lp_force_group(snum))) {
152                 BOOL is_guest = False;
153
154                 if(group_c == '+') {
155
156                         /*
157                          * Only force group if the user is a member of
158                          * the service group. Check the group memberships for
159                          * this user (we already have this) to
160                          * see if we should force the group.
161                          */
162
163                         int i;
164                         for (i = 0; i < current_user.ngroups; i++) {
165                                 if (current_user.groups[i] == conn->gid) {
166                                         gid = conn->gid;
167                                         break;
168                                 }
169                         }
170                 } else {
171                         gid = conn->gid;
172                 }
173
174                 /*
175                  * We've changed the group list in the token - we must
176                  * re-create it.
177                  */
178
179                 if (vuser && vuser->guest)
180                         is_guest = True;
181
182                 token = create_nt_token(uid, gid, current_user.ngroups, current_user.groups, is_guest);
183                 if (!token) {
184                         DEBUG(1, ("change_to_user: create_nt_token failed!\n"));
185                         return False;
186                 }
187                 must_free_token = True;
188         }
189         
190         set_sec_ctx(uid, gid, current_user.ngroups, current_user.groups, token);
191
192         /*
193          * Free the new token (as set_sec_ctx copies it).
194          */
195
196         if (must_free_token)
197                 delete_nt_token(&token);
198
199         current_user.conn = conn;
200         current_user.vuid = vuid;
201
202         DEBUG(5,("change_to_user uid=(%d,%d) gid=(%d,%d)\n",
203                  (int)getuid(),(int)geteuid(),(int)getgid(),(int)getegid()));
204   
205         return(True);
206 }
207
208 /****************************************************************************
209  Go back to being root without changing the security context stack,
210  but modify the current_user entries.
211 ****************************************************************************/
212
213 BOOL change_to_root_user(void)
214 {
215         set_root_sec_ctx();
216
217         DEBUG(5,("change_to_root_user: now uid=(%d,%d) gid=(%d,%d)\n",
218                 (int)getuid(),(int)geteuid(),(int)getgid(),(int)getegid()));
219
220         current_user.conn = NULL;
221         current_user.vuid = UID_FIELD_INVALID;
222
223         return(True);
224 }
225
226 /****************************************************************************
227  Become the user of an authenticated connected named pipe.
228  When this is called we are currently running as the connection
229  user. Doesn't modify current_user.
230 ****************************************************************************/
231
232 BOOL become_authenticated_pipe_user(pipes_struct *p)
233 {
234         if (!push_sec_ctx())
235                 return False;
236
237         set_sec_ctx(p->pipe_user.uid, p->pipe_user.gid, 
238                     p->pipe_user.ngroups, p->pipe_user.groups, p->pipe_user.nt_user_token);
239
240         return True;
241 }
242
243 /****************************************************************************
244  Unbecome the user of an authenticated connected named pipe.
245  When this is called we are running as the authenticated pipe
246  user and need to go back to being the connection user. Doesn't modify
247  current_user.
248 ****************************************************************************/
249
250 BOOL unbecome_authenticated_pipe_user(void)
251 {
252         return pop_sec_ctx();
253 }
254
255 /****************************************************************************
256  Utility functions used by become_xxx/unbecome_xxx.
257 ****************************************************************************/
258
259 struct conn_ctx {
260         connection_struct *conn;
261         uint16 vuid;
262 };
263  
264 /* A stack of current_user connection contexts. */
265  
266 static struct conn_ctx conn_ctx_stack[MAX_SEC_CTX_DEPTH];
267 static int conn_ctx_stack_ndx;
268
269 static void push_conn_ctx(void)
270 {
271         struct conn_ctx *ctx_p;
272  
273         /* Check we don't overflow our stack */
274  
275         if (conn_ctx_stack_ndx == MAX_SEC_CTX_DEPTH) {
276                 DEBUG(0, ("Connection context stack overflow!\n"));
277                 smb_panic("Connection context stack overflow!\n");
278         }
279  
280         /* Store previous user context */
281         ctx_p = &conn_ctx_stack[conn_ctx_stack_ndx];
282  
283         ctx_p->conn = current_user.conn;
284         ctx_p->vuid = current_user.vuid;
285  
286         DEBUG(3, ("push_conn_ctx(%u) : conn_ctx_stack_ndx = %d\n",
287                 (unsigned int)ctx_p->vuid, conn_ctx_stack_ndx ));
288
289         conn_ctx_stack_ndx++;
290 }
291
292 static void pop_conn_ctx(void)
293 {
294         struct conn_ctx *ctx_p;
295  
296         /* Check for stack underflow. */
297
298         if (conn_ctx_stack_ndx == 0) {
299                 DEBUG(0, ("Connection context stack underflow!\n"));
300                 smb_panic("Connection context stack underflow!\n");
301         }
302
303         conn_ctx_stack_ndx--;
304         ctx_p = &conn_ctx_stack[conn_ctx_stack_ndx];
305
306         current_user.conn = ctx_p->conn;
307         current_user.vuid = ctx_p->vuid;
308
309         ctx_p->conn = NULL;
310         ctx_p->vuid = UID_FIELD_INVALID;
311 }
312
313 void init_conn_ctx(void)
314 {
315     int i;
316  
317     /* Initialise connection context stack */
318         for (i = 0; i < MAX_SEC_CTX_DEPTH; i++) {
319                 conn_ctx_stack[i].conn = NULL;
320                 conn_ctx_stack[i].vuid = UID_FIELD_INVALID;
321     }
322 }
323
324 /****************************************************************************
325  Temporarily become a root user.  Must match with unbecome_root(). Saves and
326  restores the connection context.
327 ****************************************************************************/
328
329 void become_root(void)
330 {
331         push_sec_ctx();
332         push_conn_ctx();
333         set_root_sec_ctx();
334 }
335
336 /* Unbecome the root user */
337
338 void unbecome_root(void)
339 {
340         pop_sec_ctx();
341         pop_conn_ctx();
342 }
343
344 /****************************************************************************
345  Push the current security context then force a change via change_to_user().
346  Saves and restores the connection context.
347 ****************************************************************************/
348
349 BOOL become_user(connection_struct *conn, uint16 vuid)
350 {
351         if (!push_sec_ctx())
352                 return False;
353
354         push_conn_ctx();
355
356         if (!change_to_user(conn, vuid)) {
357                 pop_sec_ctx();
358                 pop_conn_ctx();
359                 return False;
360         }
361
362         return True;
363 }
364
365 BOOL unbecome_user(void)
366 {
367         pop_sec_ctx();
368         pop_conn_ctx();
369         return True;
370 }
371
372 /*****************************************************************
373  Convert the suplimentary SIDs returned in a netlogon into UNIX
374  group gid_t's. Add to the total group array.
375 *****************************************************************/
376  
377 void add_supplementary_nt_login_groups(int *n_groups, gid_t **pp_groups, NT_USER_TOKEN **pptok)
378 {
379         int total_groups;
380         int current_n_groups = *n_groups;
381         gid_t *final_groups = NULL;
382         size_t i;
383         NT_USER_TOKEN *ptok = *pptok;
384         NT_USER_TOKEN *new_tok = NULL;
385  
386         if (!ptok || (ptok->num_sids == 0))
387                 return;
388
389         new_tok = dup_nt_token(ptok);
390         if (!new_tok) {
391                 DEBUG(0,("add_supplementary_nt_login_groups: Failed to malloc new token\n"));
392                 return;
393         }
394         /* Leave the allocated space but empty the number of SIDs. */
395         new_tok->num_sids = 0;
396
397         total_groups = current_n_groups + ptok->num_sids;
398  
399         final_groups = (gid_t *)malloc(total_groups * sizeof(gid_t));
400         if (!final_groups) {
401                 DEBUG(0,("add_supplementary_nt_login_groups: Failed to malloc new groups.\n"));
402                 delete_nt_token(&new_tok);
403                 return;
404         }
405  
406         memcpy(final_groups, *pp_groups, current_n_groups * sizeof(gid_t));
407         for (i = 0; i < ptok->num_sids; i++) {
408                 gid_t new_grp;
409  
410                 if (NT_STATUS_IS_OK(sid_to_gid(&ptok->user_sids[i], &new_grp))) {
411                         /*
412                          * Don't add the gid_t if it is already in the current group
413                          * list. Some UNIXen don't like the same group more than once.
414                          */
415                         int j;
416
417                         for (j = 0; j < current_n_groups; j++)
418                                 if (final_groups[j] == new_grp)
419                                         break;
420                 
421                         if ( j == current_n_groups) {
422                                 /* Group not already present. */
423                                 final_groups[current_n_groups++] = new_grp;
424                         }
425                 } else {
426                         /* SID didn't map. Copy to the new token to be saved. */
427                         sid_copy(&new_tok->user_sids[new_tok->num_sids++], &ptok->user_sids[i]);
428                 }
429         }
430  
431         SAFE_FREE(*pp_groups);
432         *pp_groups = final_groups;
433         *n_groups = current_n_groups;
434
435         /* Replace the old token with the truncated one. */
436         delete_nt_token(&ptok);
437         *pptok = new_tok;
438 }
439
440 /*****************************************************************
441  *THE CANONICAL* convert name to SID function.
442  Tries local lookup first - for local domains - then uses winbind.
443 *****************************************************************/  
444
445 BOOL lookup_name(const char *domain, const char *name, DOM_SID *psid, enum SID_NAME_USE *name_type)
446 {
447         fstring sid;
448         BOOL local_lookup = False;
449         
450         *name_type = SID_NAME_UNKNOWN;
451
452         /* If we are looking up a domain user, make sure it is
453            for the local machine only */
454         
455         if (strequal(global_myname(), domain)) {
456                 local_lookup = True;
457         } else if (lp_server_role() == ROLE_DOMAIN_PDC || 
458                    lp_server_role() == ROLE_DOMAIN_BDC) {
459                 if (strequal(domain, lp_workgroup())) {
460                         local_lookup = True;
461                 }
462         }
463                 
464         if (local_lookup) {
465                 if (local_lookup_name(name, psid, name_type)) {
466                         DEBUG(10,
467                               ("lookup_name: (local) [%s]\\[%s] -> SID %s (type %s: %u)\n",
468                                domain, name, sid_to_string(sid,psid),
469                                sid_type_lookup(*name_type), (unsigned int)*name_type));
470                         return True;
471                 }
472         } else {
473                 /* Remote */
474                 if (winbind_lookup_name(domain, name, psid, name_type)) {
475                         
476                         DEBUG(10,("lookup_name (winbindd): [%s]\\[%s] -> SID %s (type %u)\n",
477                                   domain, name, sid_to_string(sid, psid), 
478                                   (unsigned int)*name_type));
479                         return True;
480                 }
481         }
482         
483         DEBUG(10, ("lookup_name: %s lookup for [%s]\\[%s] failed\n", 
484                    local_lookup ? "local" : "winbind", domain, name));
485
486         return False;
487 }
488
489 /*****************************************************************
490  *THE CANONICAL* convert SID to name function.
491  Tries local lookup first - for local sids, then tries winbind.
492 *****************************************************************/  
493
494 BOOL lookup_sid(DOM_SID *sid, fstring dom_name, fstring name, enum SID_NAME_USE *name_type)
495 {
496         if (!name_type)
497                 return False;
498
499         *name_type = SID_NAME_UNKNOWN;
500
501         /* Check if this is our own sid.  This should perhaps be done by
502            winbind?  For the moment handle it here. */
503
504         if (sid->num_auths == 5) {
505                 DOM_SID tmp_sid;
506                 uint32 rid;
507
508                 sid_copy(&tmp_sid, sid);
509                 sid_split_rid(&tmp_sid, &rid);
510
511                 if (sid_equal(get_global_sam_sid(), &tmp_sid)) {
512
513                         return map_domain_sid_to_name(&tmp_sid, dom_name) &&
514                                 local_lookup_sid(sid, name, name_type);
515                 }
516         }
517
518         if (!winbind_lookup_sid(sid, dom_name, name, name_type)) {
519                 fstring sid_str;
520                 DOM_SID tmp_sid;
521                 uint32 rid;
522
523                 DEBUG(10,("lookup_sid: winbind lookup for SID %s failed - trying local.\n", sid_to_string(sid_str, sid) ));
524
525                 sid_copy(&tmp_sid, sid);
526                 sid_split_rid(&tmp_sid, &rid);
527                 return map_domain_sid_to_name(&tmp_sid, dom_name) &&
528                         lookup_known_rid(&tmp_sid, rid, name, name_type);
529         }
530         return True;
531 }