be3f1405e433ca0ae08aaa628799023250fc96cf
[samba.git] / source / nsswitch / winbindd_acct.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind account management functions
5
6    Copyright (C) by Gerald (Jerry) Carter       2003
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24 #include "winbindd.h"
25
26 #undef DBGC_CLASS
27 #define DBGC_CLASS DBGC_WINBIND
28
29 #define WBKEY_PASSWD    "WBA_PASSWD"
30 #define WBKEY_GROUP     "WBA_GROUP"
31
32 #define NUM_PW_FIELDS   7
33 #define NUM_GRP_FIELDS  4
34
35 /* Globals */
36
37 static TDB_CONTEXT *account_tdb;
38
39 extern userdom_struct current_user_info;
40
41 struct _check_primary_grp {
42         gid_t   gid;
43         BOOL    found;
44 };
45
46 /**********************************************************************
47 **********************************************************************/
48
49 static void free_winbindd_gr( WINBINDD_GR *grp )
50 {
51         int i;
52
53         if ( !grp )
54                 return;
55                 
56         for ( i=0; i<grp->num_gr_mem; i++ )
57                 SAFE_FREE( grp->gr_mem[i] );
58
59         SAFE_FREE( grp->gr_mem );
60         
61         return;
62 }
63
64 /*****************************************************************************
65  Initialise auto-account database. 
66 *****************************************************************************/
67
68 static BOOL winbindd_accountdb_init(void)
69 {
70         /* see if we've already opened the tdb */
71         
72         if ( account_tdb )
73                 return True;
74
75         /* winbindd_idmap.tdb should always be opened by the idmap_init()
76            code first */
77
78         if ( !(account_tdb = idmap_tdb_handle()) ) {
79                 DEBUG(0, ("winbindd_accountdb_init: Unable to retreive handle for database\n"));
80                 return False;
81         }
82         
83         /* yeah! */
84         
85         return True;   
86 }
87
88 /**********************************************************************
89  Convert a string in /etc/passwd format to a struct passwd* entry
90 **********************************************************************/
91
92 static WINBINDD_PW* string2passwd( char *string )
93 {
94         static WINBINDD_PW pw;
95         char *p, *str;
96         char *fields[NUM_PW_FIELDS];
97         int i;
98         
99         if ( !string )
100                 return NULL;
101         
102         ZERO_STRUCTP( &pw );
103         
104         DEBUG(10,("string2passwd: converting \"%s\"\n", string));
105         
106         ZERO_STRUCT( fields );
107         
108         for ( i=0, str=string; i<NUM_PW_FIELDS-1; i++ ) {
109                 if ( !(p = strchr( str, ':' )) ) {
110                         DEBUG(0,("string2passwd: parsing failure\n"));
111                         return NULL;
112                 }
113                 *p = '\0';
114                 if ( str )
115                         fields[i] = str;
116                 str = p + 1;
117         }
118         if ( str ) 
119                 fields[i] = str;
120         
121         /* copy fields */
122         
123         fstrcpy( pw.pw_name,   fields[0] );
124         fstrcpy( pw.pw_passwd, fields[1] );
125         pw.pw_uid = atoi(      fields[2] );
126         pw.pw_gid = atoi(      fields[3] );
127         fstrcpy( pw.pw_gecos,  fields[4] );
128         fstrcpy( pw.pw_dir,    fields[5] );
129         fstrcpy( pw.pw_shell,  fields[6] );
130         
131         
132         /* last minute sanity checks */
133         
134         if ( pw.pw_uid==0 || pw.pw_gid==0 ) {
135                 DEBUG(0,("string2passwd: Failure! uid==%lu, gid==%lu\n",
136                         (unsigned long)pw.pw_uid, (unsigned long)pw.pw_gid));
137                 return NULL;
138         }
139         
140         DEBUG(10,("string2passwd: Success\n"));
141
142         return &pw;
143 }
144
145 /**********************************************************************
146  Convert a struct passwd* to a string formatted for /etc/passwd
147 **********************************************************************/
148
149 static char* passwd2string( const WINBINDD_PW *pw )
150 {
151         static pstring string;
152         int ret;
153         
154         if ( !pw || !pw->pw_name )
155                 return NULL;
156         
157         DEBUG(10,("passwd2string: converting passwd struct for %s\n", 
158                 pw->pw_name));
159
160         ret = pstr_sprintf( string, "%s:%s:%lu:%lu:%s:%s:%s",
161                 pw->pw_name, 
162                 pw->pw_passwd ? pw->pw_passwd : "x",
163                 (unsigned long)pw->pw_uid,
164                 (unsigned long)pw->pw_gid,
165                 pw->pw_gecos,
166                 pw->pw_dir,
167                 pw->pw_shell );
168                 
169         if ( ret < 0 ) {
170                 DEBUG(0,("passwd2string: pstr_sprintf() failed!\n"));
171                 return NULL;
172         }
173                 
174         return string;  
175 }
176
177 static void
178 add_member(const char *domain, const char *user,
179            char ***members, int *num_members)
180 {
181         fstring name;
182
183         fill_domain_username(name, domain, user);
184
185         *members = Realloc(*members, (*num_members+1) * sizeof(char **));
186
187         if (members == NULL) {
188                 DEBUG(10, ("Realloc failed\n"));
189                 return;
190         }
191
192         (*members)[*num_members] = strdup(name);
193         *num_members += 1;
194 }
195
196 /**********************************************************************
197  Add member users resulting from sid. Expand if it is a domain group.
198 **********************************************************************/
199
200 static void
201 add_expanded_sid(DOM_SID *sid, char ***members, int *num_members)
202 {
203         DOM_SID dom_sid;
204         uint32 rid;
205         struct winbindd_domain *domain;
206         int i;
207
208         char *name = NULL;
209         enum SID_NAME_USE type;
210
211         uint32 num_names;
212         DOM_SID **sid_mem;
213         char **names;
214         uint32 *types;
215
216         NTSTATUS result;
217
218         TALLOC_CTX *mem_ctx = talloc_init("add_expanded_sid");
219
220         if (mem_ctx == NULL) {
221                 DEBUG(1, ("talloc_init failed\n"));
222                 return;
223         }
224
225         sid_copy(&dom_sid, sid);
226         sid_split_rid(&dom_sid, &rid);
227
228         domain = find_domain_from_sid(&dom_sid);
229
230         if (domain == NULL) {
231                 DEBUG(3, ("Could not find domain for sid %s\n",
232                           sid_string_static(sid)));
233                 goto done;
234         }
235
236         result = domain->methods->sid_to_name(domain, mem_ctx, sid,
237                                               &name, &type);
238
239         if (!NT_STATUS_IS_OK(result)) {
240                 DEBUG(3, ("sid_to_name failed for sid %s\n",
241                           sid_string_static(sid)));
242                 goto done;
243         }
244
245         DEBUG(10, ("Found name %s, type %d\n", name, type));
246
247         if (type == SID_NAME_USER) {
248                 add_member(domain->name, name, members, num_members);
249                 goto done;
250         }
251
252         if (type != SID_NAME_DOM_GRP) {
253                 DEBUG(10, ("Alias member %s neither user nor group, ignore\n",
254                            name));
255                 goto done;
256         }
257
258         /* Expand the domain group */
259
260         result = domain->methods->lookup_groupmem(domain, mem_ctx,
261                                                   sid, &num_names,
262                                                   &sid_mem, &names,
263                                                   &types);
264
265         if (!NT_STATUS_IS_OK(result)) {
266                 DEBUG(10, ("Could not lookup group members for %s: %s\n",
267                            name, nt_errstr(result)));
268                 goto done;
269         }
270
271         for (i=0; i<num_names; i++) {
272                 DEBUG(10, ("Adding group member SID %s\n",
273                            sid_string_static(sid_mem[i])));
274
275                 if (types[i] != SID_NAME_USER) {
276                         DEBUG(1, ("Hmmm. Member %s of group %s is no user. "
277                                   "Ignoring.\n", names[i], name));
278                         continue;
279                 }
280
281                 add_member(domain->name, names[i], members, num_members);
282         }
283
284  done:
285         talloc_destroy(mem_ctx);
286         return;
287 }
288
289 /**********************************************************************
290  Add alias members. Expand them if they are domain groups.
291 **********************************************************************/
292
293 static void
294 add_expanded_alias_members(gid_t gid, char ***members, int *num_members)
295 {
296         GROUP_MAP map;
297         DOM_SID *sids = NULL;
298         int i, num_sids;
299         
300         if (!pdb_getgrgid(&map, gid)) {
301                 DEBUG(10, ("No mapping for group %d\n", gid));
302                 return;
303         }
304
305         if ( (map.sid_name_use != SID_NAME_WKN_GRP) &&
306              (map.sid_name_use != SID_NAME_ALIAS) ) {
307                 DEBUG(10, ("Group %d is no alias\n", gid));
308                 return;
309         }
310
311         if (!pdb_enum_aliasmem(&map.sid, &sids, &num_sids)) {
312                 DEBUG(10, ("Could not enum aliases for group sid %s\n",
313                            sid_string_static(&map.sid)));
314                 return;
315         }
316
317         for (i=0; i<num_sids; i++) {
318                 DEBUG(10, ("additional SID: %s\n",
319                            sid_string_static(&sids[i])));
320
321                 add_expanded_sid(&sids[i], members, num_members);
322         }
323
324         SAFE_FREE(sids);
325         return;
326 }
327
328
329 /**********************************************************************
330  Convert a string in /etc/group format to a struct group* entry
331 **********************************************************************/
332
333 static WINBINDD_GR* string2group( char *string )
334 {
335         static WINBINDD_GR grp;
336         char *p, *str;
337         char *fields[NUM_GRP_FIELDS];
338         int i;
339         char **gr_members = NULL;
340         int num_gr_members = 0;
341         
342         if ( !string )
343                 return NULL;
344                 
345         ZERO_STRUCTP( &grp );
346         
347         DEBUG(10,("string2group: converting \"%s\"\n", string));
348         
349         ZERO_STRUCT( fields );
350         
351         for ( i=0, str=string; i<NUM_GRP_FIELDS-1; i++ ) {
352                 if ( !(p = strchr( str, ':' )) ) {
353                         DEBUG(0,("string2group: parsing failure\n"));
354                         return NULL;
355                 }
356                 *p = '\0';
357                 if ( str )
358                         fields[i] = str;
359                 str = p + 1;
360         }
361         
362         /* group members */
363         
364         if ( *str ) {
365                 /* we already know we have a non-empty string */
366
367                 num_gr_members = count_chars(str, ',') + 1;
368                 
369                 /* if there was at least one comma, then there 
370                    are n+1 members */
371                 if ( num_gr_members ) {
372                         fstring buffer;
373                         
374                         gr_members = (char**)smb_xmalloc(sizeof(char*)*(num_gr_members+1));
375                         
376                         i = 0;
377                         while ( next_token(&str, buffer, ",", sizeof(buffer)) && i<num_gr_members ) {
378                                 gr_members[i++] = smb_xstrdup(buffer);
379                         }
380
381                         gr_members[i]   = NULL;
382                 }
383         }
384
385         
386         /* copy fields */
387         
388         fstrcpy( grp.gr_name,   fields[0] );
389         fstrcpy( grp.gr_passwd, fields[1] );
390         grp.gr_gid = atoi(      fields[2] );
391
392         add_expanded_alias_members(grp.gr_gid, &gr_members, &num_gr_members);
393         
394         grp.num_gr_mem = num_gr_members;
395         grp.gr_mem     = gr_members;
396         
397         /* last minute sanity checks */
398         
399         if ( grp.gr_gid == 0 ) {
400                 DEBUG(0,("string2group: Failure! gid==%lu\n", (unsigned long)grp.gr_gid));
401                 SAFE_FREE( gr_members );
402                 return NULL;
403         }
404         
405         DEBUG(10,("string2group: Success\n"));
406
407         return &grp;
408 }
409
410 /**********************************************************************
411  Convert a struct group* to a string formatted for /etc/group
412 **********************************************************************/
413
414 static char* group2string( const WINBINDD_GR *grp )
415 {
416         static pstring string;
417         int ret;
418         char *member, *gr_mem_str;
419         int num_members;
420         int i, size;
421         
422         if ( !grp || !grp->gr_name )
423                 return NULL;
424         
425         DEBUG(10,("group2string: converting passwd struct for %s\n", 
426                 grp->gr_name));
427         
428         if ( grp->num_gr_mem ) {
429                 int idx = 0;
430
431                 member = grp->gr_mem[0];
432                 size = 0;
433                 num_members = 0;
434
435                 while ( member ) {
436                         size += strlen(member) + 1;
437                         num_members++;
438                         member = grp->gr_mem[num_members];
439                 }
440                 
441                 gr_mem_str = smb_xmalloc(size);
442         
443                 for ( i=0; i<num_members; i++ ) {
444                         snprintf( &gr_mem_str[idx], size-idx, "%s,", grp->gr_mem[i] );
445                         idx += strlen(grp->gr_mem[i]) + 1;
446                 }
447                 /* add trailing NULL (also removes trailing ',' */
448                 gr_mem_str[size-1] = '\0';
449         }
450         else {
451                 /* no members */
452                 gr_mem_str = smb_xmalloc(sizeof(fstring));
453                 fstrcpy( gr_mem_str, "" );
454         }
455
456         ret = pstr_sprintf( string, "%s:%s:%lu:%s",
457                 grp->gr_name, 
458                 grp->gr_passwd ? grp->gr_passwd : "*",
459                 (unsigned long)grp->gr_gid,
460                 gr_mem_str );
461                 
462         SAFE_FREE( gr_mem_str );
463                 
464         if ( ret < 0 ) {
465                 DEBUG(0,("group2string: pstr_sprintf() failed!\n"));
466                 return NULL;
467         }
468                 
469         return string;  
470 }
471
472 /**********************************************************************
473 **********************************************************************/
474
475 static char* acct_userkey_byname( const char *name )
476 {
477         static fstring key;
478         
479         fstr_sprintf( key, "%s/NAME/%s", WBKEY_PASSWD, name );
480         
481         return key;             
482 }
483
484 /**********************************************************************
485 **********************************************************************/
486
487 static char* acct_userkey_byuid( uid_t uid )
488 {
489         static fstring key;
490         
491         fstr_sprintf( key, "%s/UID/%lu", WBKEY_PASSWD, (unsigned long)uid );
492         
493         return key;             
494 }
495
496 /**********************************************************************
497 **********************************************************************/
498
499 static char* acct_groupkey_byname( const char *name )
500 {
501         static fstring key;
502         
503         fstr_sprintf( key, "%s/NAME/%s", WBKEY_GROUP, name );
504         
505         return key;             
506 }
507
508 /**********************************************************************
509 **********************************************************************/
510
511 static char* acct_groupkey_bygid( gid_t gid )
512 {
513         static fstring key;
514         
515         fstr_sprintf( key, "%s/GID/%lu", WBKEY_GROUP, (unsigned long)gid );
516         
517         return key;             
518 }
519
520 /**********************************************************************
521 **********************************************************************/
522
523 WINBINDD_PW* wb_getpwnam( const char * name )
524 {
525         char *keystr;
526         TDB_DATA data;
527         static WINBINDD_PW *pw;
528         
529         if ( !account_tdb && !winbindd_accountdb_init() ) {
530                 DEBUG(0,("wb_getpwnam: Failed to open winbindd account db\n"));
531                 return NULL;
532         }
533                 
534         
535         keystr = acct_userkey_byname( name );
536         
537         data = tdb_fetch_bystring( account_tdb, keystr );
538         
539         pw = NULL;
540         
541         if ( data.dptr ) {
542                 pw = string2passwd( data.dptr );
543                 SAFE_FREE( data.dptr );
544         }
545                 
546         DEBUG(5,("wb_getpwnam: %s user (%s)\n", 
547                 (pw ? "Found" : "Did not find"), name ));
548         
549         return pw;
550 }
551
552 /**********************************************************************
553 **********************************************************************/
554
555 WINBINDD_PW* wb_getpwuid( const uid_t uid )
556 {
557         char *keystr;
558         TDB_DATA data;
559         static WINBINDD_PW *pw;
560         
561         if ( !account_tdb && !winbindd_accountdb_init() ) {
562                 DEBUG(0,("wb_getpwuid: Failed to open winbindd account db\n"));
563                 return NULL;
564         }
565         
566         data = tdb_fetch_bystring( account_tdb, acct_userkey_byuid(uid) );
567         if ( !data.dptr ) {
568                 DEBUG(4,("wb_getpwuid: failed to locate uid == %lu\n", (unsigned long)uid));
569                 return NULL;
570         }
571         keystr = acct_userkey_byname( data.dptr );
572
573         SAFE_FREE( data.dptr );
574         
575         data = tdb_fetch_bystring( account_tdb, keystr );
576         
577         pw = NULL;
578         
579         if ( data.dptr ) {
580                 pw = string2passwd( data.dptr );
581                 SAFE_FREE( data.dptr );
582         }
583
584         DEBUG(5,("wb_getpwuid: %s user (uid == %lu)\n", 
585                 (pw ? "Found" : "Did not find"), (unsigned long)uid ));
586         
587         return pw;
588 }
589
590 /**********************************************************************
591 **********************************************************************/
592
593 static BOOL wb_storepwnam( const WINBINDD_PW *pw )
594 {
595         char *namekey, *uidkey;
596         TDB_DATA data;
597         char *str;
598         int ret = 0;
599         fstring username;
600
601         if ( !account_tdb && !winbindd_accountdb_init() ) {
602                 DEBUG(0,("wb_storepwnam: Failed to open winbindd account db\n"));
603                 return False;
604         }
605
606         namekey = acct_userkey_byname( pw->pw_name );
607         
608         /* lock the main entry first */
609         
610         if ( tdb_lock_bystring(account_tdb, namekey, 0) == -1 ) {
611                 DEBUG(0,("wb_storepwnam: Failed to lock %s\n", namekey));
612                 return False;
613         }
614         
615         str = passwd2string( pw );
616
617         data.dptr = str;
618         data.dsize = strlen(str) + 1;   
619
620         if ( (tdb_store_bystring(account_tdb, namekey, data, TDB_REPLACE)) == -1 ) {
621                 DEBUG(0,("wb_storepwnam: Failed to store \"%s\"\n", str));
622                 ret = -1;
623                 goto done;
624         }
625         
626         /* store the uid index */
627         
628         uidkey = acct_userkey_byuid(pw->pw_uid);
629         
630         fstrcpy( username, pw->pw_name );
631         data.dptr = username;
632         data.dsize = strlen(username) + 1;
633         
634         if ( (tdb_store_bystring(account_tdb, uidkey, data, TDB_REPLACE)) == -1 ) {
635                 DEBUG(0,("wb_storepwnam: Failed to store uid key \"%s\"\n", str));
636                 tdb_delete_bystring(account_tdb, namekey);
637                 ret = -1;
638                 goto done;
639         }               
640         
641         DEBUG(10,("wb_storepwnam: Success -> \"%s\"\n", str));
642
643 done:   
644         tdb_unlock_bystring( account_tdb, namekey );
645         
646         return ( ret == 0 );
647 }
648
649 /**********************************************************************
650 **********************************************************************/
651
652 WINBINDD_GR* wb_getgrnam( const char * name )
653 {
654         char *keystr;
655         TDB_DATA data;
656         static WINBINDD_GR *grp;
657         
658         if ( !account_tdb && !winbindd_accountdb_init() ) {
659                 DEBUG(0,("wb_getgrnam: Failed to open winbindd account db\n"));
660                 return NULL;
661         }
662                 
663         
664         keystr = acct_groupkey_byname( name );
665         
666         data = tdb_fetch_bystring( account_tdb, keystr );
667         
668         grp = NULL;
669         
670         if ( data.dptr ) {
671                 grp = string2group( data.dptr );
672                 SAFE_FREE( data.dptr );
673         }
674                 
675         DEBUG(5,("wb_getgrnam: %s group (%s)\n", 
676                 (grp ? "Found" : "Did not find"), name ));
677         
678         return grp;
679 }
680
681 /**********************************************************************
682 **********************************************************************/
683
684 WINBINDD_GR* wb_getgrgid( gid_t gid )
685 {
686         char *keystr;
687         TDB_DATA data;
688         static WINBINDD_GR *grp;
689         
690         if ( !account_tdb && !winbindd_accountdb_init() ) {
691                 DEBUG(0,("wb_getgrgid: Failed to open winbindd account db\n"));
692                 return NULL;
693         }
694         
695         data = tdb_fetch_bystring( account_tdb, acct_groupkey_bygid(gid) );
696         if ( !data.dptr ) {
697                 DEBUG(4,("wb_getgrgid: failed to locate gid == %lu\n", 
698                          (unsigned long)gid));
699                 return NULL;
700         }
701         keystr = acct_groupkey_byname( data.dptr );
702
703         SAFE_FREE( data.dptr );
704         
705         data = tdb_fetch_bystring( account_tdb, keystr );
706         
707         grp = NULL;
708         
709         if ( data.dptr ) {
710                 grp = string2group( data.dptr );
711                 SAFE_FREE( data.dptr );
712         }
713
714         DEBUG(5,("wb_getgrgid: %s group (gid == %lu)\n", 
715                 (grp ? "Found" : "Did not find"), (unsigned long)gid ));
716         
717         return grp;
718 }
719
720 /**********************************************************************
721 **********************************************************************/
722
723 static BOOL wb_storegrnam( const WINBINDD_GR *grp )
724 {
725         char *namekey, *gidkey;
726         TDB_DATA data;
727         char *str;
728         int ret = 0;
729         fstring groupname;
730
731         if ( !account_tdb && !winbindd_accountdb_init() ) {
732                 DEBUG(0,("wb_storepwnam: Failed to open winbindd account db\n"));
733                 return False;
734         }
735
736         namekey = acct_groupkey_byname( grp->gr_name );
737         
738         /* lock the main entry first */
739         
740         if ( tdb_lock_bystring(account_tdb, namekey, 0) == -1 ) {
741                 DEBUG(0,("wb_storegrnam: Failed to lock %s\n", namekey));
742                 return False;
743         }
744         
745         str = group2string( grp );
746
747         data.dptr = str;
748         data.dsize = strlen(str) + 1;   
749
750         if ( (tdb_store_bystring(account_tdb, namekey, data, TDB_REPLACE)) == -1 ) {
751                 DEBUG(0,("wb_storegrnam: Failed to store \"%s\"\n", str));
752                 ret = -1;
753                 goto done;
754         }
755         
756         /* store the gid index */
757         
758         gidkey = acct_groupkey_bygid(grp->gr_gid);
759         
760         fstrcpy( groupname, grp->gr_name );
761         data.dptr = groupname;
762         data.dsize = strlen(groupname) + 1;
763         
764         if ( (tdb_store_bystring(account_tdb, gidkey, data, TDB_REPLACE)) == -1 ) {
765                 DEBUG(0,("wb_storegrnam: Failed to store gid key \"%s\"\n", str));
766                 tdb_delete_bystring(account_tdb, namekey);
767                 ret = -1;
768                 goto done;
769         }
770         
771         DEBUG(10,("wb_storegrnam: Success -> \"%s\"\n", str));
772
773 done:   
774         tdb_unlock_bystring( account_tdb, namekey );
775         
776         return ( ret == 0 );
777 }
778
779 /**********************************************************************
780 **********************************************************************/
781
782 static BOOL wb_addgrpmember( WINBINDD_GR *grp, const char *user )
783 {
784         int i;
785         char **members;
786         
787         if ( !grp || !user )
788                 return False;
789         
790         for ( i=0; i<grp->num_gr_mem; i++ ) {
791                 if ( StrCaseCmp( grp->gr_mem[i], user ) == 0 )
792                         return True;
793         }
794         
795         /* add one new slot and keep an extra for the terminating NULL */
796         members = Realloc( grp->gr_mem, (grp->num_gr_mem+2)*sizeof(char*) );
797         if ( !members )
798                 return False;
799                 
800         grp->gr_mem = members;
801         grp->gr_mem[grp->num_gr_mem++] = smb_xstrdup(user);
802         grp->gr_mem[grp->num_gr_mem]   = NULL;
803                 
804         return True;
805 }
806
807 /**********************************************************************
808 **********************************************************************/
809
810 static BOOL wb_delgrpmember( WINBINDD_GR *grp, const char *user )
811 {
812         int i;
813         BOOL found = False;
814         
815         if ( !grp || !user )
816                 return False;
817         
818         for ( i=0; i<grp->num_gr_mem; i++ ) {
819                 if ( StrCaseCmp( grp->gr_mem[i], user ) == 0 ) {
820                         found = True;
821                         break;
822                 }
823         }
824         
825         if ( !found ) 
826                 return False;
827
828         /* still some remaining members */
829
830         if ( grp->num_gr_mem > 1 ) {
831                 SAFE_FREE(grp->gr_mem[i]);
832                 grp->num_gr_mem--;
833                 grp->gr_mem[i] = grp->gr_mem[grp->num_gr_mem];
834                 grp->gr_mem[grp->num_gr_mem] = NULL;
835         }
836         else {  /* last one */
837                 free_winbindd_gr( grp );
838                 grp->gr_mem = NULL;
839                 grp->num_gr_mem = 0;
840         }
841                                 
842         return True;
843 }
844
845 /**********************************************************************
846 **********************************************************************/
847
848 static int cleangroups_traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
849                        void *state)
850 {
851         int len;
852         fstring key;
853         char *name = (char*)state;
854         
855         fstr_sprintf( key, "%s/NAME", WBKEY_GROUP );
856         len = strlen(key);
857         
858         /* if this is a group entry then, check the members */
859         
860         if ( (strncmp(kbuf.dptr, key, len) == 0) && dbuf.dptr ) {
861                 WINBINDD_GR *grp;
862                 
863                 if ( !(grp = string2group( dbuf.dptr )) ) {
864                         DEBUG(0,("cleangroups_traverse_fn: Failure to parse [%s]\n",
865                                 dbuf.dptr));
866                         return 0;
867                 }
868                 
869                 /* just try to delete the user and rely on wb_delgrpmember()
870                    to tell you whether or not the group changed.  This is more 
871                    effecient than testing group membership first since the 
872                    checks for deleting a user from a group is essentially the 
873                    same as checking if he/she is a member */
874                    
875                 if ( wb_delgrpmember( grp, name ) ) {
876                         DEBUG(10,("cleanupgroups_traverse_fn: Removed user (%s) from group (%s)\n",
877                                 name, grp->gr_name));
878                         wb_storegrnam( grp );
879                 }
880                 
881                 free_winbindd_gr( grp );
882         }
883
884         return 0;
885 }
886
887 /**********************************************************************
888 **********************************************************************/
889
890 static BOOL wb_delete_user( WINBINDD_PW *pw)
891 {
892         char *namekey;
893         char *uidkey;
894         
895         if ( !account_tdb && !winbindd_accountdb_init() ) {
896                 DEBUG(0,("wb_delete_user: Failed to open winbindd account db\n"));
897                 return False;
898         }
899
900         namekey = acct_userkey_byname( pw->pw_name );
901         
902         /* lock the main entry first */
903         
904         if ( tdb_lock_bystring(account_tdb, namekey, 0) == -1 ) {
905                 DEBUG(0,("wb_delete_user: Failed to lock %s\n", namekey));
906                 return False;
907         }
908         
909         /* remove user from all groups */
910         
911         tdb_traverse(account_tdb, cleangroups_traverse_fn, (void *)pw->pw_name);
912         
913         /* remove the user */
914         uidkey = acct_userkey_byuid( pw->pw_uid );
915         
916         tdb_delete_bystring( account_tdb, namekey );
917         tdb_delete_bystring( account_tdb, uidkey );
918         
919         tdb_unlock_bystring( account_tdb, namekey );
920         
921         return True;
922 }
923
924 /**********************************************************************
925 **********************************************************************/
926
927 static int isprimarygroup_traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, 
928                                       TDB_DATA dbuf, void *params)
929 {
930         int len;
931         fstring key;
932         struct _check_primary_grp *check = (struct _check_primary_grp*)params;
933         
934         fstr_sprintf( key, "%s/NAME", WBKEY_PASSWD );
935         len = strlen(key);
936         
937         /* if this is a group entry then, check the members */
938         
939         if ( (strncmp(kbuf.dptr, key, len) == 0) && dbuf.dptr ) {
940                 WINBINDD_PW *pw;;
941                 
942                 if ( !(pw = string2passwd( dbuf.dptr )) ) {
943                         DEBUG(0,("isprimarygroup_traverse_fn: Failure to parse [%s]\n",
944                                 dbuf.dptr));
945                         return 0;
946                 }
947                 
948                 if ( check->gid == pw->pw_gid ) {
949                         check->found = True;
950                         return 1;
951                 }
952         }
953
954         return 0;
955 }
956
957
958 /**********************************************************************
959 **********************************************************************/
960
961 static BOOL wb_delete_group( WINBINDD_GR *grp )
962 {
963         struct _check_primary_grp check;
964         char *namekey;
965         char *gidkey;
966         
967         if ( !account_tdb && !winbindd_accountdb_init() ) {
968                 DEBUG(0,("wb_delete_group: Failed to open winbindd account db\n"));
969                 return False;
970         }
971         
972         /* lock the main entry first */
973         
974         namekey = acct_groupkey_byname( grp->gr_name ); 
975         if ( tdb_lock_bystring(account_tdb, namekey, 0) == -1 ) {
976                 DEBUG(0,("wb_delete_group: Failed to lock %s\n", namekey));
977                 return False;
978         }
979         
980         /* is this group the primary group for any user?  If 
981            so deny delete */
982            
983         check.found = False;    
984         tdb_traverse(account_tdb, isprimarygroup_traverse_fn, (void *)&check);
985         
986         if ( check.found ) {
987                 DEBUG(4,("wb_delete_group: Cannot delete group (%s) since it "
988                         "is the primary group for some users\n", grp->gr_name));
989                 return False;
990         }
991         
992         /* We're clear.  Delete the group */
993         
994         DEBUG(5,("wb_delete_group: Removing group (%s)\n", grp->gr_name));
995         
996         gidkey = acct_groupkey_bygid( grp->gr_gid );
997         
998         tdb_delete_bystring( account_tdb, namekey );
999         tdb_delete_bystring( account_tdb, gidkey );
1000         
1001         tdb_unlock_bystring( account_tdb, namekey );
1002         
1003         return True;
1004 }
1005
1006 /**********************************************************************
1007  Create a new "UNIX" user for the system given a username
1008 **********************************************************************/
1009
1010 enum winbindd_result winbindd_create_user(struct winbindd_cli_state *state)
1011 {
1012         char *user, *group;
1013         unid_t id;
1014         WINBINDD_PW pw, *pw_check;
1015         WINBINDD_GR *wb_grp;
1016         struct group *unix_grp;
1017         gid_t primary_gid;
1018         uint32 flags = state->request.flags;
1019         uint32 rid;
1020         
1021         if ( !state->privileged ) {
1022                 DEBUG(2, ("winbindd_create_user: non-privileged access denied!\n"));
1023                 return WINBINDD_ERROR;
1024         }
1025         
1026         /* Ensure null termination */
1027         state->request.data.acct_mgt.username[sizeof(state->request.data.acct_mgt.username)-1]='\0';
1028         state->request.data.acct_mgt.groupname[sizeof(state->request.data.acct_mgt.groupname)-1]='\0';
1029         
1030         user  = state->request.data.acct_mgt.username;
1031         group = state->request.data.acct_mgt.groupname;
1032         
1033         DEBUG(3, ("[%5lu]: create_user: user=>(%s), group=>(%s)\n", 
1034                 (unsigned long)state->pid, user, group));
1035
1036         if ( (pw_check=wb_getpwnam(user)) != NULL ) {
1037                 DEBUG(0,("winbindd_create_user: Refusing to create user that already exists (%s)\n", 
1038                         user));
1039                 return WINBINDD_ERROR;
1040         }
1041
1042                 
1043         if ( !*group )
1044                 group = lp_template_primary_group();
1045                 
1046         /* validate the primary group
1047            1) lookup in local tdb first
1048            2) call getgrnam() as a last resort */
1049            
1050         if ( (wb_grp=wb_getgrnam(group)) != NULL ) {
1051                 primary_gid = wb_grp->gr_gid;
1052                 free_winbindd_gr( wb_grp );
1053         }
1054         else if ( (unix_grp=sys_getgrnam(group)) != NULL ) {
1055                 primary_gid = unix_grp->gr_gid; 
1056         }
1057         else {
1058                 DEBUG(2,("winbindd_create_user: Cannot validate gid for group (%s)\n", group));
1059                 return WINBINDD_ERROR;
1060         }
1061
1062         /* get a new uid */
1063         
1064         if ( !NT_STATUS_IS_OK(idmap_allocate_id( &id, ID_USERID)) ) {
1065                 DEBUG(0,("winbindd_create_user: idmap_allocate_id() failed!\n"));
1066                 return WINBINDD_ERROR;
1067         }
1068         
1069         /* The substitution of %U and %D in the 'template homedir' is done
1070            by lp_string() calling standard_sub_basic(). */
1071
1072         fstrcpy( current_user_info.smb_name, user );
1073         sub_set_smb_name( user );
1074         fstrcpy( current_user_info.domain, get_global_sam_name() );
1075         
1076         /* fill in the passwd struct */
1077                 
1078         fstrcpy( pw.pw_name,   user );
1079         fstrcpy( pw.pw_passwd, "x" );
1080         fstrcpy( pw.pw_gecos,  user);
1081         fstrcpy( pw.pw_dir,    lp_template_homedir() );
1082         fstrcpy( pw.pw_shell,  lp_template_shell() );
1083         
1084         pw.pw_uid = id.uid;
1085         pw.pw_gid = primary_gid;
1086         
1087         /* store the new entry */
1088         
1089         if ( !wb_storepwnam(&pw) )
1090                 return WINBINDD_ERROR;
1091                 
1092         /* do we need a new RID? */
1093         
1094         if ( flags & WBFLAG_ALLOCATE_RID ) {
1095                 if ( !NT_STATUS_IS_OK(idmap_allocate_rid(&rid, USER_RID_TYPE)) ) {
1096                         DEBUG(0,("winbindd_create_user: RID allocation failure!  Cannot create user (%s)\n",
1097                                 user));
1098                         wb_delete_user( &pw );
1099                         
1100                         return WINBINDD_ERROR;
1101                 }
1102                 
1103                 state->response.data.rid = rid;
1104         }
1105
1106         return WINBINDD_OK;
1107 }
1108
1109 /**********************************************************************
1110  Create a new "UNIX" group for the system given a username
1111 **********************************************************************/
1112
1113 enum winbindd_result winbindd_create_group(struct winbindd_cli_state *state)
1114 {
1115         char *group;
1116         unid_t id;
1117         WINBINDD_GR grp, *grp_check;
1118         uint32 flags = state->request.flags;
1119         uint32 rid;
1120         
1121         if ( !state->privileged ) {
1122                 DEBUG(2, ("winbindd_create_group: non-privileged access denied!\n"));
1123                 return WINBINDD_ERROR;
1124         }
1125         
1126         /* Ensure null termination */
1127         state->request.data.acct_mgt.groupname[sizeof(state->request.data.acct_mgt.groupname)-1]='\0';  
1128         group = state->request.data.acct_mgt.groupname;
1129         
1130         DEBUG(3, ("[%5lu]: create_group: (%s)\n", (unsigned long)state->pid, group));
1131         
1132         if ( (grp_check=wb_getgrnam(group)) != NULL ) {
1133                 DEBUG(0,("winbindd_create_group: Refusing to create group that already exists (%s)\n", 
1134                         group));
1135                 return WINBINDD_ERROR;
1136         }
1137         
1138         /* get a new gid */
1139         
1140         if ( !NT_STATUS_IS_OK(idmap_allocate_id( &id, ID_GROUPID)) ) {
1141                 DEBUG(0,("winbindd_create_group: idmap_allocate_id() failed!\n"));
1142                 return WINBINDD_ERROR;
1143         }
1144         
1145         /* fill in the group struct */
1146                 
1147         fstrcpy( grp.gr_name,   group );
1148         fstrcpy( grp.gr_passwd, "*" );
1149         
1150         grp.gr_gid      = id.gid;
1151         grp.gr_mem      = NULL; /* start with no members */
1152         grp.num_gr_mem  = 0;
1153         
1154         if ( !wb_storegrnam(&grp) )
1155                 return WINBINDD_ERROR;
1156                 
1157         /* do we need a new RID? */
1158         
1159         if ( flags & WBFLAG_ALLOCATE_RID ) {
1160                 if ( !NT_STATUS_IS_OK(idmap_allocate_rid(&rid, GROUP_RID_TYPE)) ) {
1161                         DEBUG(0,("winbindd_create_group: RID allocation failure!  Cannot create group (%s)\n",
1162                                 group));
1163                         wb_delete_group( &grp );
1164                         
1165                         return WINBINDD_ERROR;
1166                 }
1167                 
1168                 state->response.data.rid = rid;
1169         }
1170
1171         return WINBINDD_OK;
1172 }
1173
1174 /**********************************************************************
1175  Add a user to the membership for a group.
1176 **********************************************************************/
1177
1178 enum winbindd_result winbindd_add_user_to_group(struct winbindd_cli_state *state)
1179 {
1180         WINBINDD_PW *pw;
1181         WINBINDD_GR *grp;
1182         char *user, *group;
1183         BOOL ret;
1184         
1185         if ( !state->privileged ) {
1186                 DEBUG(2, ("winbindd_add_user_to_group: non-privileged access denied!\n"));
1187                 return WINBINDD_ERROR;
1188         }
1189         
1190         /* Ensure null termination */
1191         state->request.data.acct_mgt.groupname[sizeof(state->request.data.acct_mgt.groupname)-1]='\0';  
1192         state->request.data.acct_mgt.username[sizeof(state->request.data.acct_mgt.username)-1]='\0';    
1193         group = state->request.data.acct_mgt.groupname;
1194         user = state->request.data.acct_mgt.username;
1195         
1196         DEBUG(3, ("[%5lu]:  add_user_to_group: add %s to %s\n", (unsigned long)state->pid, 
1197                 user, group));
1198         
1199         /* make sure it is a valid user */
1200         
1201         if ( !(pw = wb_getpwnam( user )) ) {
1202                 DEBUG(4,("winbindd_add_user_to_group: Cannot add a non-existent user\n"));
1203                 return WINBINDD_ERROR;
1204         }
1205         
1206         /* make sure it is a valid group */
1207         
1208         if ( !(grp = wb_getgrnam( group )) ) {
1209                 DEBUG(4,("winbindd_add_user_to_group: Cannot add a user to a non-extistent group\n"));
1210                 return WINBINDD_ERROR;  
1211         }
1212         
1213         if ( !wb_addgrpmember( grp, user ) )
1214                 return WINBINDD_ERROR;
1215                 
1216         ret = wb_storegrnam(grp);
1217         
1218         free_winbindd_gr( grp );
1219         
1220         return ( ret ? WINBINDD_OK : WINBINDD_ERROR );
1221 }
1222
1223 /**********************************************************************
1224  Remove a user from the membership of a group
1225 **********************************************************************/
1226
1227 enum winbindd_result winbindd_remove_user_from_group(struct winbindd_cli_state *state)
1228 {
1229         WINBINDD_GR *grp;
1230         char *user, *group;
1231         BOOL ret;
1232
1233         if ( !state->privileged ) {
1234                 DEBUG(2, ("winbindd_remove_user_from_group: non-privileged access denied!\n"));
1235                 return WINBINDD_ERROR;
1236         }
1237         
1238         /* Ensure null termination */
1239         state->request.data.acct_mgt.groupname[sizeof(state->request.data.acct_mgt.groupname)-1]='\0';  
1240         state->request.data.acct_mgt.username[sizeof(state->request.data.acct_mgt.username)-1]='\0';    
1241         group = state->request.data.acct_mgt.groupname;
1242         user = state->request.data.acct_mgt.username;
1243         
1244         DEBUG(3, ("[%5lu]:  remove_user_from_group: delete %s from %s\n", (unsigned long)state->pid, 
1245                 user, group));
1246         
1247         /* don't worry about checking the username since we're removing it anyways */
1248         
1249         /* make sure it is a valid group */
1250         
1251         if ( !(grp = wb_getgrnam( group )) ) {
1252                 DEBUG(4,("winbindd_remove_user_from_group: Cannot remove a user from a non-extistent group\n"));
1253                 return WINBINDD_ERROR;  
1254         }
1255         
1256         if ( !wb_delgrpmember( grp, user ) )
1257                 return WINBINDD_ERROR;
1258                 
1259         ret = wb_storegrnam(grp);
1260         
1261         free_winbindd_gr( grp );
1262         
1263         return ( ret ? WINBINDD_OK : WINBINDD_ERROR );
1264 }
1265
1266 /**********************************************************************
1267  Set the primary group membership of a user
1268 **********************************************************************/
1269
1270 enum winbindd_result winbindd_set_user_primary_group(struct winbindd_cli_state *state)
1271 {
1272         WINBINDD_PW *pw;
1273         WINBINDD_GR *grp;
1274         char *user, *group;
1275
1276         if ( !state->privileged ) {
1277                 DEBUG(2, ("winbindd_set_user_primary_group: non-privileged access denied!\n"));
1278                 return WINBINDD_ERROR;
1279         }
1280         
1281         /* Ensure null termination */
1282         state->request.data.acct_mgt.groupname[sizeof(state->request.data.acct_mgt.groupname)-1]='\0';  
1283         state->request.data.acct_mgt.username[sizeof(state->request.data.acct_mgt.username)-1]='\0';    
1284         group = state->request.data.acct_mgt.groupname;
1285         user = state->request.data.acct_mgt.username;
1286         
1287         DEBUG(3, ("[%5lu]:  set_user_primary_group: group %s for user %s\n", 
1288                   (unsigned long)state->pid, group, user));
1289         
1290         /* make sure it is a valid user */
1291         
1292         if ( !(pw = wb_getpwnam( user )) ) {
1293                 DEBUG(4,("winbindd_add_user_to_group: Cannot add a non-existent user\n"));
1294                 return WINBINDD_ERROR;
1295         }
1296         
1297         /* make sure it is a valid group */
1298         
1299         if ( !(grp = wb_getgrnam( group )) ) {
1300                 DEBUG(4,("winbindd_add_user_to_group: Cannot add a user to a non-extistent group\n"));
1301                 return WINBINDD_ERROR;  
1302         }
1303         
1304         pw->pw_gid = grp->gr_gid;
1305
1306         free_winbindd_gr( grp );
1307                 
1308         return ( wb_storepwnam(pw) ? WINBINDD_OK : WINBINDD_ERROR );
1309 }
1310
1311 /**********************************************************************
1312  Delete a user from the winbindd account tdb.
1313 **********************************************************************/
1314
1315 enum winbindd_result winbindd_delete_user(struct winbindd_cli_state *state)
1316 {
1317         WINBINDD_PW *pw;
1318         char *user;
1319
1320         if ( !state->privileged ) {
1321                 DEBUG(2, ("winbindd_delete_user: non-privileged access denied!\n"));
1322                 return WINBINDD_ERROR;
1323         }
1324         
1325         /* Ensure null termination */
1326         state->request.data.acct_mgt.username[sizeof(state->request.data.acct_mgt.username)-1]='\0';    
1327         user = state->request.data.acct_mgt.username;
1328         
1329         DEBUG(3, ("[%5lu]:  delete_user: %s\n", (unsigned long)state->pid, user));
1330         
1331         /* make sure it is a valid user */
1332         
1333         if ( !(pw = wb_getpwnam( user )) ) {
1334                 DEBUG(4,("winbindd_delete_user: Cannot delete a non-existent user\n"));
1335                 return WINBINDD_ERROR;
1336         }
1337         
1338         return ( wb_delete_user(pw) ? WINBINDD_OK : WINBINDD_ERROR );
1339 }
1340
1341 /**********************************************************************
1342  Delete a group from winbindd's account tdb. 
1343 **********************************************************************/
1344
1345 enum winbindd_result winbindd_delete_group(struct winbindd_cli_state *state)
1346 {
1347         WINBINDD_GR *grp;
1348         char *group;
1349         BOOL ret;
1350
1351         if ( !state->privileged ) {
1352                 DEBUG(2, ("winbindd_delete_group: non-privileged access denied!\n"));
1353                 return WINBINDD_ERROR;
1354         }
1355         
1356         /* Ensure null termination */
1357         state->request.data.acct_mgt.username[sizeof(state->request.data.acct_mgt.groupname)-1]='\0';   
1358         group = state->request.data.acct_mgt.groupname;
1359         
1360         DEBUG(3, ("[%5lu]:  delete_group: %s\n", (unsigned long)state->pid, group));
1361         
1362         /* make sure it is a valid group */
1363         
1364         if ( !(grp = wb_getgrnam( group )) ) {
1365                 DEBUG(4,("winbindd_delete_group: Cannot delete a non-existent group\n"));
1366                 return WINBINDD_ERROR;
1367         }
1368         
1369         ret = wb_delete_group(grp);
1370         
1371         free_winbindd_gr( grp );
1372         
1373         return ( ret ? WINBINDD_OK : WINBINDD_ERROR );
1374 }
1375
1376 static void add_string_to_array(char *name, char ***names, int *num_names)
1377 {
1378         *names = Realloc(*names, (*num_names + 1) * sizeof(char **));
1379
1380         if (*names == NULL)
1381                 return;
1382
1383         (*names)[*num_names] = name;
1384         *num_names += 1;
1385 }
1386
1387 /**********************************************************************
1388  List all group names locally defined
1389 **********************************************************************/
1390
1391 void wb_list_group_names(char ***names, int *num_names)
1392 {
1393         TDB_LIST_NODE *nodes, *node;
1394         
1395         if (!winbindd_accountdb_init())
1396                 return;
1397
1398         nodes = tdb_search_keys(account_tdb, acct_groupkey_byname("*"));
1399
1400         node = nodes;
1401
1402         while (node != NULL) {
1403                 char *name = (char *)node->node_key.dptr;
1404
1405                 DEBUG(10, ("Found key %s\n", name));
1406
1407                 node = node->next;
1408
1409                 /* Skip WBA_GROUP */
1410                 name = strchr(name, '/');
1411                 if (name == NULL)
1412                         continue;
1413                 name += 1;
1414
1415                 /* Skip NAME */
1416                 name = strchr(name, '/');
1417                 if (name == NULL)
1418                         continue;
1419                 name += 1;
1420
1421                 DEBUG(10, ("adding %s\n", name));
1422
1423                 add_string_to_array(strdup(name), names, num_names);
1424         }
1425
1426         tdb_search_list_free(nodes);
1427 }