This adds winbind-generated groups showing up in 'getent group'. It is not
[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 && !found; i++ ) {
819                 if ( StrCaseCmp( grp->gr_mem[i], user ) == 0 ) 
820                         found = True;
821         }
822         
823         if ( !found ) 
824                 return False;
825
826         /* still some remaining members */
827
828         if ( grp->num_gr_mem > 1 ) {
829                 memmove( grp->gr_mem[i], grp->gr_mem[i+1], sizeof(char*)*(grp->num_gr_mem-(i+1)) );
830                 grp->num_gr_mem--;
831         }
832         else {  /* last one */
833                 free_winbindd_gr( grp );
834                 grp->gr_mem = NULL;
835                 grp->num_gr_mem = 0;
836         }
837                                 
838         return True;
839 }
840
841 /**********************************************************************
842 **********************************************************************/
843
844 static int cleangroups_traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
845                        void *state)
846 {
847         int len;
848         fstring key;
849         char *name = (char*)state;
850         
851         fstr_sprintf( key, "%s/NAME", WBKEY_GROUP );
852         len = strlen(key);
853         
854         /* if this is a group entry then, check the members */
855         
856         if ( (strncmp(kbuf.dptr, key, len) == 0) && dbuf.dptr ) {
857                 WINBINDD_GR *grp;
858                 
859                 if ( !(grp = string2group( dbuf.dptr )) ) {
860                         DEBUG(0,("cleangroups_traverse_fn: Failure to parse [%s]\n",
861                                 dbuf.dptr));
862                         return 0;
863                 }
864                 
865                 /* just try to delete the user and rely on wb_delgrpmember()
866                    to tell you whether or not the group changed.  This is more 
867                    effecient than testing group membership first since the 
868                    checks for deleting a user from a group is essentially the 
869                    same as checking if he/she is a member */
870                    
871                 if ( wb_delgrpmember( grp, name ) ) {
872                         DEBUG(10,("cleanupgroups_traverse_fn: Removed user (%s) from group (%s)\n",
873                                 name, grp->gr_name));
874                         wb_storegrnam( grp );
875                 }
876                 
877                 free_winbindd_gr( grp );
878         }
879
880         return 0;
881 }
882
883 /**********************************************************************
884 **********************************************************************/
885
886 static BOOL wb_delete_user( WINBINDD_PW *pw)
887 {
888         char *namekey;
889         char *uidkey;
890         
891         if ( !account_tdb && !winbindd_accountdb_init() ) {
892                 DEBUG(0,("wb_delete_user: Failed to open winbindd account db\n"));
893                 return False;
894         }
895
896         namekey = acct_userkey_byname( pw->pw_name );
897         
898         /* lock the main entry first */
899         
900         if ( tdb_lock_bystring(account_tdb, namekey, 0) == -1 ) {
901                 DEBUG(0,("wb_delete_user: Failed to lock %s\n", namekey));
902                 return False;
903         }
904         
905         /* remove user from all groups */
906         
907         tdb_traverse(account_tdb, cleangroups_traverse_fn, (void *)pw->pw_name);
908         
909         /* remove the user */
910         uidkey = acct_userkey_byuid( pw->pw_uid );
911         
912         tdb_delete_bystring( account_tdb, namekey );
913         tdb_delete_bystring( account_tdb, uidkey );
914         
915         tdb_unlock_bystring( account_tdb, namekey );
916         
917         return True;
918 }
919
920 /**********************************************************************
921 **********************************************************************/
922
923 static int isprimarygroup_traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, 
924                                       TDB_DATA dbuf, void *params)
925 {
926         int len;
927         fstring key;
928         struct _check_primary_grp *check = (struct _check_primary_grp*)params;
929         
930         fstr_sprintf( key, "%s/NAME", WBKEY_PASSWD );
931         len = strlen(key);
932         
933         /* if this is a group entry then, check the members */
934         
935         if ( (strncmp(kbuf.dptr, key, len) == 0) && dbuf.dptr ) {
936                 WINBINDD_PW *pw;;
937                 
938                 if ( !(pw = string2passwd( dbuf.dptr )) ) {
939                         DEBUG(0,("isprimarygroup_traverse_fn: Failure to parse [%s]\n",
940                                 dbuf.dptr));
941                         return 0;
942                 }
943                 
944                 if ( check->gid == pw->pw_gid ) {
945                         check->found = True;
946                         return 1;
947                 }
948         }
949
950         return 0;
951 }
952
953
954 /**********************************************************************
955 **********************************************************************/
956
957 static BOOL wb_delete_group( WINBINDD_GR *grp )
958 {
959         struct _check_primary_grp check;
960         char *namekey;
961         char *gidkey;
962         
963         if ( !account_tdb && !winbindd_accountdb_init() ) {
964                 DEBUG(0,("wb_delete_group: Failed to open winbindd account db\n"));
965                 return False;
966         }
967         
968         /* lock the main entry first */
969         
970         namekey = acct_groupkey_byname( grp->gr_name ); 
971         if ( tdb_lock_bystring(account_tdb, namekey, 0) == -1 ) {
972                 DEBUG(0,("wb_delete_group: Failed to lock %s\n", namekey));
973                 return False;
974         }
975         
976         /* is this group the primary group for any user?  If 
977            so deny delete */
978            
979         check.found = False;    
980         tdb_traverse(account_tdb, isprimarygroup_traverse_fn, (void *)&check);
981         
982         if ( check.found ) {
983                 DEBUG(4,("wb_delete_group: Cannot delete group (%s) since it "
984                         "is the primary group for some users\n", grp->gr_name));
985                 return False;
986         }
987         
988         /* We're clear.  Delete the group */
989         
990         DEBUG(5,("wb_delete_group: Removing group (%s)\n", grp->gr_name));
991         
992         gidkey = acct_groupkey_bygid( grp->gr_gid );
993         
994         tdb_delete_bystring( account_tdb, namekey );
995         tdb_delete_bystring( account_tdb, gidkey );
996         
997         tdb_unlock_bystring( account_tdb, namekey );
998         
999         return True;
1000 }
1001
1002 /**********************************************************************
1003  Create a new "UNIX" user for the system given a username
1004 **********************************************************************/
1005
1006 enum winbindd_result winbindd_create_user(struct winbindd_cli_state *state)
1007 {
1008         char *user, *group;
1009         unid_t id;
1010         WINBINDD_PW pw;
1011         WINBINDD_GR *wb_grp;
1012         struct group *unix_grp;
1013         gid_t primary_gid;
1014         uint32 flags = state->request.flags;
1015         uint32 rid;
1016         
1017         if ( !state->privileged ) {
1018                 DEBUG(2, ("winbindd_create_user: non-privileged access denied!\n"));
1019                 return WINBINDD_ERROR;
1020         }
1021         
1022         /* Ensure null termination */
1023         state->request.data.acct_mgt.username[sizeof(state->request.data.acct_mgt.username)-1]='\0';
1024         state->request.data.acct_mgt.groupname[sizeof(state->request.data.acct_mgt.groupname)-1]='\0';
1025         
1026         user  = state->request.data.acct_mgt.username;
1027         group = state->request.data.acct_mgt.groupname;
1028         
1029         DEBUG(3, ("[%5lu]: create_user: user=>(%s), group=>(%s)\n", 
1030                 (unsigned long)state->pid, user, group));
1031                 
1032         if ( !*group )
1033                 group = lp_template_primary_group();
1034                 
1035         /* validate the primary group
1036            1) lookup in local tdb first
1037            2) call getgrnam() as a last resort */
1038            
1039         if ( (wb_grp=wb_getgrnam(group)) != NULL ) {
1040                 primary_gid = wb_grp->gr_gid;
1041                 free_winbindd_gr( wb_grp );
1042         }
1043         else if ( (unix_grp=sys_getgrnam(group)) != NULL ) {
1044                 primary_gid = unix_grp->gr_gid; 
1045         }
1046         else {
1047                 DEBUG(2,("winbindd_create_user: Cannot validate gid for group (%s)\n", group));
1048                 return WINBINDD_ERROR;
1049         }
1050
1051         /* get a new uid */
1052         
1053         if ( !NT_STATUS_IS_OK(idmap_allocate_id( &id, ID_USERID)) ) {
1054                 DEBUG(0,("winbindd_create_user: idmap_allocate_id() failed!\n"));
1055                 return WINBINDD_ERROR;
1056         }
1057         
1058         /* The substitution of %U and %D in the 'template homedir' is done
1059            by lp_string() calling standard_sub_basic(). */
1060
1061         fstrcpy( current_user_info.smb_name, user );
1062         sub_set_smb_name( user );
1063         fstrcpy( current_user_info.domain, get_global_sam_name() );
1064         
1065         /* fill in the passwd struct */
1066                 
1067         fstrcpy( pw.pw_name,   user );
1068         fstrcpy( pw.pw_passwd, "x" );
1069         fstrcpy( pw.pw_gecos,  user);
1070         fstrcpy( pw.pw_dir,    lp_template_homedir() );
1071         fstrcpy( pw.pw_shell,  lp_template_shell() );
1072         
1073         pw.pw_uid = id.uid;
1074         pw.pw_gid = primary_gid;
1075         
1076         /* store the new entry */
1077         
1078         if ( !wb_storepwnam(&pw) )
1079                 return WINBINDD_ERROR;
1080                 
1081         /* do we need a new RID? */
1082         
1083         if ( flags & WBFLAG_ALLOCATE_RID ) {
1084                 if ( !NT_STATUS_IS_OK(idmap_allocate_rid(&rid, USER_RID_TYPE)) ) {
1085                         DEBUG(0,("winbindd_create_user: RID allocation failure!  Cannot create user (%s)\n",
1086                                 user));
1087                         wb_delete_user( &pw );
1088                         
1089                         return WINBINDD_ERROR;
1090                 }
1091                 
1092                 state->response.data.rid = rid;
1093         }
1094
1095         return WINBINDD_OK;
1096 }
1097
1098 /**********************************************************************
1099  Create a new "UNIX" group for the system given a username
1100 **********************************************************************/
1101
1102 enum winbindd_result winbindd_create_group(struct winbindd_cli_state *state)
1103 {
1104         char *group;
1105         unid_t id;
1106         WINBINDD_GR grp;
1107         uint32 flags = state->request.flags;
1108         uint32 rid;
1109         
1110         if ( !state->privileged ) {
1111                 DEBUG(2, ("winbindd_create_group: non-privileged access denied!\n"));
1112                 return WINBINDD_ERROR;
1113         }
1114         
1115         /* Ensure null termination */
1116         state->request.data.acct_mgt.groupname[sizeof(state->request.data.acct_mgt.groupname)-1]='\0';  
1117         group = state->request.data.acct_mgt.groupname;
1118         
1119         DEBUG(3, ("[%5lu]: create_group: (%s)\n", (unsigned long)state->pid, group));
1120         
1121         /* get a new uid */
1122         
1123         if ( !NT_STATUS_IS_OK(idmap_allocate_id( &id, ID_GROUPID)) ) {
1124                 DEBUG(0,("winbindd_create_group: idmap_allocate_id() failed!\n"));
1125                 return WINBINDD_ERROR;
1126         }
1127         
1128         /* fill in the group struct */
1129                 
1130         fstrcpy( grp.gr_name,   group );
1131         fstrcpy( grp.gr_passwd, "*" );
1132         
1133         grp.gr_gid      = id.gid;
1134         grp.gr_mem      = NULL; /* start with no members */
1135         grp.num_gr_mem  = 0;
1136         
1137         if ( !wb_storegrnam(&grp) )
1138                 return WINBINDD_ERROR;
1139                 
1140         /* do we need a new RID? */
1141         
1142         if ( flags & WBFLAG_ALLOCATE_RID ) {
1143                 if ( !NT_STATUS_IS_OK(idmap_allocate_rid(&rid, GROUP_RID_TYPE)) ) {
1144                         DEBUG(0,("winbindd_create_group: RID allocation failure!  Cannot create group (%s)\n",
1145                                 group));
1146                         wb_delete_group( &grp );
1147                         
1148                         return WINBINDD_ERROR;
1149                 }
1150                 
1151                 state->response.data.rid = rid;
1152         }
1153
1154         return WINBINDD_OK;
1155 }
1156
1157 /**********************************************************************
1158  Add a user to the membership for a group.
1159 **********************************************************************/
1160
1161 enum winbindd_result winbindd_add_user_to_group(struct winbindd_cli_state *state)
1162 {
1163         WINBINDD_PW *pw;
1164         WINBINDD_GR *grp;
1165         char *user, *group;
1166         BOOL ret;
1167         
1168         if ( !state->privileged ) {
1169                 DEBUG(2, ("winbindd_add_user_to_group: non-privileged access denied!\n"));
1170                 return WINBINDD_ERROR;
1171         }
1172         
1173         /* Ensure null termination */
1174         state->request.data.acct_mgt.groupname[sizeof(state->request.data.acct_mgt.groupname)-1]='\0';  
1175         state->request.data.acct_mgt.username[sizeof(state->request.data.acct_mgt.username)-1]='\0';    
1176         group = state->request.data.acct_mgt.groupname;
1177         user = state->request.data.acct_mgt.username;
1178         
1179         DEBUG(3, ("[%5lu]:  add_user_to_group: add %s to %s\n", (unsigned long)state->pid, 
1180                 user, group));
1181         
1182         /* make sure it is a valid user */
1183         
1184         if ( !(pw = wb_getpwnam( user )) ) {
1185                 DEBUG(4,("winbindd_add_user_to_group: Cannot add a non-existent user\n"));
1186                 return WINBINDD_ERROR;
1187         }
1188         
1189         /* make sure it is a valid group */
1190         
1191         if ( !(grp = wb_getgrnam( group )) ) {
1192                 DEBUG(4,("winbindd_add_user_to_group: Cannot add a user to a non-extistent group\n"));
1193                 return WINBINDD_ERROR;  
1194         }
1195         
1196         if ( !wb_addgrpmember( grp, user ) )
1197                 return WINBINDD_ERROR;
1198                 
1199         ret = wb_storegrnam(grp);
1200         
1201         free_winbindd_gr( grp );
1202         
1203         return ( ret ? WINBINDD_OK : WINBINDD_ERROR );
1204 }
1205
1206 /**********************************************************************
1207  Remove a user from the membership of a group
1208 **********************************************************************/
1209
1210 enum winbindd_result winbindd_remove_user_from_group(struct winbindd_cli_state *state)
1211 {
1212         WINBINDD_GR *grp;
1213         char *user, *group;
1214         BOOL ret;
1215
1216         if ( !state->privileged ) {
1217                 DEBUG(2, ("winbindd_remove_user_from_group: non-privileged access denied!\n"));
1218                 return WINBINDD_ERROR;
1219         }
1220         
1221         /* Ensure null termination */
1222         state->request.data.acct_mgt.groupname[sizeof(state->request.data.acct_mgt.groupname)-1]='\0';  
1223         state->request.data.acct_mgt.username[sizeof(state->request.data.acct_mgt.username)-1]='\0';    
1224         group = state->request.data.acct_mgt.groupname;
1225         user = state->request.data.acct_mgt.username;
1226         
1227         DEBUG(3, ("[%5lu]:  remove_user_to_group: delete %s from %s\n", (unsigned long)state->pid, 
1228                 user, group));
1229         
1230         /* don't worry about checking the username since we're removing it anyways */
1231         
1232         /* make sure it is a valid group */
1233         
1234         if ( !(grp = wb_getgrnam( group )) ) {
1235                 DEBUG(4,("winbindd_remove_user_to_group: Cannot remove a user to a non-extistent group\n"));
1236                 return WINBINDD_ERROR;  
1237         }
1238         
1239         if ( !wb_delgrpmember( grp, user ) )
1240                 return WINBINDD_ERROR;
1241                 
1242         ret = wb_storegrnam(grp);
1243         
1244         free_winbindd_gr( grp );
1245         
1246         return ( ret ? WINBINDD_OK : WINBINDD_ERROR );
1247 }
1248
1249 /**********************************************************************
1250  Set the primary group membership of a user
1251 **********************************************************************/
1252
1253 enum winbindd_result winbindd_set_user_primary_group(struct winbindd_cli_state *state)
1254 {
1255         WINBINDD_PW *pw;
1256         WINBINDD_GR *grp;
1257         char *user, *group;
1258
1259         if ( !state->privileged ) {
1260                 DEBUG(2, ("winbindd_set_user_primary_group: non-privileged access denied!\n"));
1261                 return WINBINDD_ERROR;
1262         }
1263         
1264         /* Ensure null termination */
1265         state->request.data.acct_mgt.groupname[sizeof(state->request.data.acct_mgt.groupname)-1]='\0';  
1266         state->request.data.acct_mgt.username[sizeof(state->request.data.acct_mgt.username)-1]='\0';    
1267         group = state->request.data.acct_mgt.groupname;
1268         user = state->request.data.acct_mgt.username;
1269         
1270         DEBUG(3, ("[%5lu]:  set_user_primary_group: group %s for user %s\n", 
1271                   (unsigned long)state->pid, group, user));
1272         
1273         /* make sure it is a valid user */
1274         
1275         if ( !(pw = wb_getpwnam( user )) ) {
1276                 DEBUG(4,("winbindd_add_user_to_group: Cannot add a non-existent user\n"));
1277                 return WINBINDD_ERROR;
1278         }
1279         
1280         /* make sure it is a valid group */
1281         
1282         if ( !(grp = wb_getgrnam( group )) ) {
1283                 DEBUG(4,("winbindd_add_user_to_group: Cannot add a user to a non-extistent group\n"));
1284                 return WINBINDD_ERROR;  
1285         }
1286         
1287         pw->pw_gid = grp->gr_gid;
1288
1289         free_winbindd_gr( grp );
1290                 
1291         return ( wb_storepwnam(pw) ? WINBINDD_OK : WINBINDD_ERROR );
1292 }
1293
1294 /**********************************************************************
1295  Delete a user from the winbindd account tdb.
1296 **********************************************************************/
1297
1298 enum winbindd_result winbindd_delete_user(struct winbindd_cli_state *state)
1299 {
1300         WINBINDD_PW *pw;
1301         char *user;
1302
1303         if ( !state->privileged ) {
1304                 DEBUG(2, ("winbindd_delete_user: non-privileged access denied!\n"));
1305                 return WINBINDD_ERROR;
1306         }
1307         
1308         /* Ensure null termination */
1309         state->request.data.acct_mgt.username[sizeof(state->request.data.acct_mgt.username)-1]='\0';    
1310         user = state->request.data.acct_mgt.username;
1311         
1312         DEBUG(3, ("[%5lu]:  delete_user: %s\n", (unsigned long)state->pid, user));
1313         
1314         /* make sure it is a valid user */
1315         
1316         if ( !(pw = wb_getpwnam( user )) ) {
1317                 DEBUG(4,("winbindd_delete_user: Cannot delete a non-existent user\n"));
1318                 return WINBINDD_ERROR;
1319         }
1320         
1321         return ( wb_delete_user(pw) ? WINBINDD_OK : WINBINDD_ERROR );
1322 }
1323
1324 /**********************************************************************
1325  Delete a group from winbindd's account tdb. 
1326 **********************************************************************/
1327
1328 enum winbindd_result winbindd_delete_group(struct winbindd_cli_state *state)
1329 {
1330         WINBINDD_GR *grp;
1331         char *group;
1332         BOOL ret;
1333
1334         if ( !state->privileged ) {
1335                 DEBUG(2, ("winbindd_delete_group: non-privileged access denied!\n"));
1336                 return WINBINDD_ERROR;
1337         }
1338         
1339         /* Ensure null termination */
1340         state->request.data.acct_mgt.username[sizeof(state->request.data.acct_mgt.groupname)-1]='\0';   
1341         group = state->request.data.acct_mgt.groupname;
1342         
1343         DEBUG(3, ("[%5lu]:  delete_group: %s\n", (unsigned long)state->pid, group));
1344         
1345         /* make sure it is a valid group */
1346         
1347         if ( !(grp = wb_getgrnam( group )) ) {
1348                 DEBUG(4,("winbindd_delete_group: Cannot delete a non-existent group\n"));
1349                 return WINBINDD_ERROR;
1350         }
1351         
1352         ret = wb_delete_group(grp);
1353         
1354         free_winbindd_gr( grp );
1355         
1356         return ( ret ? WINBINDD_OK : WINBINDD_ERROR );
1357 }
1358
1359 static void add_string_to_array(char *name, char ***names, int *num_names)
1360 {
1361         *names = Realloc(*names, (*num_names + 1) * sizeof(char **));
1362
1363         if (*names == NULL)
1364                 return;
1365
1366         (*names)[*num_names] = name;
1367         *num_names += 1;
1368 }
1369
1370 /**********************************************************************
1371  List all group names locally defined
1372 **********************************************************************/
1373
1374 void wb_list_group_names(char ***names, int *num_names)
1375 {
1376         TDB_LIST_NODE *nodes, *node;
1377         
1378         if (!winbindd_accountdb_init())
1379                 return;
1380
1381         nodes = tdb_search_keys(account_tdb, acct_groupkey_byname("*"));
1382
1383         node = nodes;
1384
1385         while (node != NULL) {
1386                 char *name = (char *)node->node_key.dptr;
1387
1388                 DEBUG(10, ("Found key %s\n", name));
1389
1390                 node = node->next;
1391
1392                 /* Skip WBA_GROUP */
1393                 name = strchr(name, '/');
1394                 if (name == NULL)
1395                         continue;
1396                 name += 1;
1397
1398                 /* Skip NAME */
1399                 name = strchr(name, '/');
1400                 if (name == NULL)
1401                         continue;
1402                 name += 1;
1403
1404                 DEBUG(10, ("adding %s\n", name));
1405
1406                 add_string_to_array(strdup(name), names, num_names);
1407         }
1408
1409         tdb_search_list_free(nodes);
1410 }