Caching user, group and domain sam handles was a stupid idea.
[ira/wip.git] / source3 / nsswitch / winbind_nss.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 2.0
4
5    Windows NT Domain nsswitch module
6
7    Copyright (C) Tim Potter 2000
8    
9    This library is free software; you can redistribute it and/or
10    modify it under the terms of the GNU Library General Public
11    License as published by the Free Software Foundation; either
12    version 2 of the License, or (at your option) any later version.
13    
14    This library is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17    Library General Public License for more details.
18    
19    You should have received a copy of the GNU Library General Public
20    License along with this library; if not, write to the
21    Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22    Boston, MA  02111-1307, USA.   
23 */
24
25 #include "winbind_nss_config.h"
26 #include "winbindd_nss.h"
27
28 /* Prototypes from common.c */
29
30 void init_request(struct winbindd_request *req,int rq_type);
31 NSS_STATUS winbindd_request(int req_type, 
32                                  struct winbindd_request *request,
33                                  struct winbindd_response *response);
34 int write_sock(void *buffer, int count);
35 int read_reply(struct winbindd_response *response);
36 void free_response(struct winbindd_response *response);
37
38 /* Allocate some space from the nss static buffer.  The buffer and buflen
39    are the pointers passed in by the C library to the _nss_ntdom_*
40    functions. */
41
42 static char *get_static(char **buffer, int *buflen, int len)
43 {
44         char *result;
45
46         /* Error check.  We return false if things aren't set up right, or
47            there isn't enough buffer space left. */
48         
49         if ((buffer == NULL) || (buflen == NULL) || (*buflen < len)) {
50                 return NULL;
51         }
52
53         /* Some architectures, like Sparc, need pointers aligned on 
54            boundaries */
55 #if _ALIGNMENT_REQUIRED
56         {
57                 int mod = len % _MAX_ALIGNMENT;
58                 if(mod != 0)
59                         len += _MAX_ALIGNMENT - mod;
60         }
61 #endif
62
63         /* Return an index into the static buffer */
64
65         result = *buffer;
66         *buffer += len;
67         *buflen -= len;
68
69         return result;
70 }
71
72 /* I've copied the strtok() replacement function next_token() from
73    lib/util_str.c as I really don't want to have to link in any other
74    objects if I can possibly avoid it. */
75
76 BOOL next_token(char **ptr,char *buff,char *sep, size_t bufsize)
77 {
78         char *s;
79         BOOL quoted;
80         size_t len=1;
81
82         if (!ptr) return(False);
83
84         s = *ptr;
85
86         /* default to simple separators */
87         if (!sep) sep = " \t\n\r";
88
89         /* find the first non sep char */
90         while (*s && strchr(sep,*s)) s++;
91         
92         /* nothing left? */
93         if (! *s) return(False);
94         
95         /* copy over the token */
96         for (quoted = False; len < bufsize && *s && (quoted || !strchr(sep,*s)); s++) {
97                 if (*s == '\"') {
98                         quoted = !quoted;
99                 } else {
100                         len++;
101                         *buff++ = *s;
102                 }
103         }
104         
105         *ptr = (*s) ? s+1 : s;  
106         *buff = 0;
107         
108         return(True);
109 }
110
111
112 /* Fill a pwent structure from a winbindd_response structure.  We use
113    the static data passed to us by libc to put strings and stuff in.
114    Return NSS_STATUS_TRYAGAIN if we run out of memory. */
115
116 static NSS_STATUS fill_pwent(struct passwd *result,
117                                   struct winbindd_pw *pw,
118                                   char **buffer, int *buflen)
119 {
120         /* User name */
121
122         if ((result->pw_name = 
123              get_static(buffer, buflen, strlen(pw->pw_name) + 1)) == NULL) {
124
125                 /* Out of memory */
126
127                 return NSS_STATUS_TRYAGAIN;
128         }
129
130         strcpy(result->pw_name, pw->pw_name);
131
132         /* Password */
133
134         if ((result->pw_passwd = 
135              get_static(buffer, buflen, strlen(pw->pw_passwd) + 1)) == NULL) {
136
137                 /* Out of memory */
138
139                 return NSS_STATUS_TRYAGAIN;
140         }
141
142         strcpy(result->pw_passwd, pw->pw_passwd);
143         
144         /* [ug]id */
145
146         result->pw_uid = pw->pw_uid;
147         result->pw_gid = pw->pw_gid;
148
149         /* GECOS */
150
151         if ((result->pw_gecos = 
152              get_static(buffer, buflen, strlen(pw->pw_gecos) + 1)) == NULL) {
153
154                 /* Out of memory */
155
156                 return NSS_STATUS_TRYAGAIN;
157         }
158
159         strcpy(result->pw_gecos, pw->pw_gecos);
160         
161         /* Home directory */
162         
163         if ((result->pw_dir = 
164              get_static(buffer, buflen, strlen(pw->pw_dir) + 1)) == NULL) {
165
166                 /* Out of memory */
167
168                 return NSS_STATUS_TRYAGAIN;
169         }
170
171         strcpy(result->pw_dir, pw->pw_dir);
172
173         /* Logon shell */
174         
175         if ((result->pw_shell = 
176              get_static(buffer, buflen, strlen(pw->pw_shell) + 1)) == NULL) {
177                 
178                 /* Out of memory */
179
180                 return NSS_STATUS_TRYAGAIN;
181         }
182
183         strcpy(result->pw_shell, pw->pw_shell);
184
185         return NSS_STATUS_SUCCESS;
186 }
187
188 /* Fill a grent structure from a winbindd_response structure.  We use
189    the static data passed to us by libc to put strings and stuff in.
190    Return NSS_STATUS_TRYAGAIN if we run out of memory. */
191
192 static int fill_grent(struct group *result, struct winbindd_gr *gr,
193                       char *gr_mem, char **buffer, int *buflen)
194 {
195         fstring name;
196         int i;
197
198         /* Group name */
199
200         if ((result->gr_name =
201              get_static(buffer, buflen, strlen(gr->gr_name) + 1)) == NULL) {
202
203                 /* Out of memory */
204
205                 return NSS_STATUS_TRYAGAIN;
206         }
207
208         strcpy(result->gr_name, gr->gr_name);
209
210         /* Password */
211
212         if ((result->gr_passwd =
213              get_static(buffer, buflen, strlen(gr->gr_passwd) + 1)) == NULL) {
214
215                 /* Out of memory */
216                 
217                 return NSS_STATUS_TRYAGAIN;
218         }
219
220         strcpy(result->gr_passwd, gr->gr_passwd);
221
222         /* gid */
223
224         result->gr_gid = gr->gr_gid;
225
226         /* Group membership */
227
228         if ((gr->num_gr_mem < 0) || !gr_mem) {
229                 gr->num_gr_mem = 0;
230         }
231
232         if ((result->gr_mem = 
233              (char **)get_static(buffer, buflen, (gr->num_gr_mem + 1) * 
234                                  sizeof(char *))) == NULL) {
235
236                 /* Out of memory */
237
238                 return NSS_STATUS_TRYAGAIN;
239         }
240
241         if (gr->num_gr_mem == 0) {
242
243                 /* Group is empty */
244
245                 *(result->gr_mem) = NULL;
246                 return NSS_STATUS_SUCCESS;
247         }
248
249         /* Start looking at extra data */
250
251         i = 0;
252
253         while(next_token((char **)&gr_mem, name, ",", sizeof(fstring))) {
254         
255                 /* Allocate space for member */
256         
257                 if (((result->gr_mem)[i] = 
258                      get_static(buffer, buflen, strlen(name) + 1)) == NULL) {
259             
260                         /* Out of memory */
261             
262                         return NSS_STATUS_TRYAGAIN;
263                 }        
264         
265                 strcpy((result->gr_mem)[i], name);
266                 i++;
267         }
268
269         /* Terminate list */
270
271         (result->gr_mem)[i] = NULL;
272
273         return NSS_STATUS_SUCCESS;
274 }
275
276 /*
277  * NSS user functions
278  */
279
280 static struct winbindd_response getpwent_response;
281
282 static int ndx_pw_cache;                 /* Current index into pwd cache */
283 static int num_pw_cache;                 /* Current size of pwd cache */
284
285 /* Rewind "file pointer" to start of ntdom password database */
286
287 NSS_STATUS
288 _nss_winbind_setpwent(void)
289 {
290 #ifdef DEBUG_NSS
291         fprintf(stderr, "[%5d]: setpwent\n", getpid());
292 #endif
293
294         if (num_pw_cache > 0) {
295                 ndx_pw_cache = num_pw_cache = 0;
296                 free_response(&getpwent_response);
297         }
298
299         return winbindd_request(WINBINDD_SETPWENT, NULL, NULL);
300 }
301
302 /* Close ntdom password database "file pointer" */
303
304 NSS_STATUS
305 _nss_winbind_endpwent(void)
306 {
307 #ifdef DEBUG_NSS
308         fprintf(stderr, "[%5d]: endpwent\n", getpid());
309 #endif
310
311         if (num_pw_cache > 0) {
312                 ndx_pw_cache = num_pw_cache = 0;
313                 free_response(&getpwent_response);
314         }
315
316         return winbindd_request(WINBINDD_ENDPWENT, NULL, NULL);
317 }
318
319 /* Fetch the next password entry from ntdom password database */
320
321 #define MAX_GETPWENT_USERS 250
322
323 NSS_STATUS
324 _nss_winbind_getpwent_r(struct passwd *result, char *buffer, 
325                         size_t buflen, int *errnop)
326 {
327         NSS_STATUS ret;
328         struct winbindd_request request;
329         static int called_again;
330
331 #ifdef DEBUG_NSS
332         fprintf(stderr, "[%5d]: getpwent\n", getpid());
333 #endif
334
335         /* Return an entry from the cache if we have one, or if we are
336            called again because we exceeded our static buffer.  */
337
338         if ((ndx_pw_cache < num_pw_cache) || called_again) {
339                 goto return_result;
340         }
341
342         /* Else call winbindd to get a bunch of entries */
343         
344         if (num_pw_cache > 0) {
345                 free_response(&getpwent_response);
346         }
347
348         ZERO_STRUCT(request);
349         ZERO_STRUCT(getpwent_response);
350
351         request.data.num_entries = MAX_GETPWENT_USERS;
352
353         ret = winbindd_request(WINBINDD_GETPWENT, &request, 
354                                &getpwent_response);
355
356         if (ret == NSS_STATUS_SUCCESS) {
357                 struct winbindd_pw *pw_cache;
358
359                 /* Fill cache */
360
361                 ndx_pw_cache = 0;
362                 num_pw_cache = getpwent_response.data.num_entries;
363
364                 /* Return a result */
365
366         return_result:
367
368                 pw_cache = getpwent_response.extra_data;
369
370                 /* Check data is valid */
371
372                 if (pw_cache == NULL) {
373                         return NSS_STATUS_NOTFOUND;
374                 }
375
376                 ret = fill_pwent(result, &pw_cache[ndx_pw_cache],
377                                  &buffer, &buflen);
378                 
379                 /* Out of memory - try again */
380
381                 if (ret == NSS_STATUS_TRYAGAIN) {
382                         called_again = True;
383                         *errnop = errno = ERANGE;
384                         return ret;
385                 }
386
387                 *errnop = errno = 0;
388                 called_again = False;
389                 ndx_pw_cache++;
390
391                 /* If we've finished with this lot of results free cache */
392
393                 if (ndx_pw_cache == num_pw_cache) {
394                         ndx_pw_cache = num_pw_cache = 0;
395                         free_response(&getpwent_response);
396                 }
397         }
398
399         return ret;
400 }
401
402 /* Return passwd struct from uid */
403
404 NSS_STATUS
405 _nss_winbind_getpwuid_r(uid_t uid, struct passwd *result, char *buffer,
406                         size_t buflen, int *errnop)
407 {
408         NSS_STATUS ret;
409         static struct winbindd_response response;
410         struct winbindd_request request;
411         static int keep_response=0;
412
413         /* If our static buffer needs to be expanded we are called again */
414         if (!keep_response) {
415
416                 /* Call for the first time */
417
418                 ZERO_STRUCT(response);
419                 ZERO_STRUCT(request);
420
421                 request.data.uid = uid;
422
423                 ret = winbindd_request(WINBINDD_GETPWNAM_FROM_UID, &request, 
424                                        &response);
425
426                 if (ret == NSS_STATUS_SUCCESS) {
427                         ret = fill_pwent(result, &response.data.pw, 
428                                          &buffer, &buflen);
429
430                         if (ret == NSS_STATUS_TRYAGAIN) {
431                                 keep_response = True;
432                                 *errnop = errno = ERANGE;
433                                 return ret;
434                         }
435                 }
436
437         } else {
438
439                 /* We've been called again */
440
441                 ret = fill_pwent(result, &response.data.pw, &buffer, &buflen);
442
443                 if (ret == NSS_STATUS_TRYAGAIN) {
444                         keep_response = True;
445                         *errnop = errno = ERANGE;
446                         return ret;
447                 }
448
449                 keep_response = False;
450                 *errnop = errno = 0;
451         }
452
453         free_response(&response);
454         return ret;
455 }
456
457 /* Return passwd struct from username */
458
459 NSS_STATUS
460 _nss_winbind_getpwnam_r(const char *name, struct passwd *result, char *buffer,
461                         size_t buflen, int *errnop)
462 {
463         NSS_STATUS ret;
464         static struct winbindd_response response;
465         struct winbindd_request request;
466         static int keep_response;
467
468 #ifdef DEBUG_NSS
469         fprintf(stderr, "[%5d]: getpwnam %s\n", getpid(), name);
470 #endif
471
472         /* If our static buffer needs to be expanded we are called again */
473
474         if (!keep_response) {
475
476                 /* Call for the first time */
477
478                 ZERO_STRUCT(response);
479                 ZERO_STRUCT(request);
480
481                 strncpy(request.data.username, name, 
482                         sizeof(request.data.username) - 1);
483                 request.data.username
484                         [sizeof(request.data.username) - 1] = '\0';
485
486                 ret = winbindd_request(WINBINDD_GETPWNAM_FROM_USER, &request, 
487                                        &response);
488
489                 if (ret == NSS_STATUS_SUCCESS) {
490                         ret = fill_pwent(result, &response.data.pw, &buffer,
491                                          &buflen);
492
493                         if (ret == NSS_STATUS_TRYAGAIN) {
494                                 keep_response = True;
495                                 *errnop = errno = ERANGE;
496                                 return ret;
497                         }
498                 }
499
500         } else {
501
502                 /* We've been called again */
503
504                 ret = fill_pwent(result, &response.data.pw, &buffer, &buflen);
505
506                 if (ret == NSS_STATUS_TRYAGAIN) {
507                         keep_response = True;
508                         *errnop = errno = ERANGE;
509                         return ret;
510                 }
511
512                 keep_response = False;
513                 *errnop = errno = 0;
514         }
515
516         free_response(&response);
517         return ret;
518 }
519
520 /*
521  * NSS group functions
522  */
523
524 static struct winbindd_response getgrent_response;
525
526 static int ndx_gr_cache;                 /* Current index into grp cache */
527 static int num_gr_cache;                 /* Current size of grp cache */
528
529 /* Rewind "file pointer" to start of ntdom group database */
530
531 NSS_STATUS
532 _nss_winbind_setgrent(void)
533 {
534 #ifdef DEBUG_NSS
535         fprintf(stderr, "[%5d]: setgrent\n", getpid());
536 #endif
537
538         if (num_gr_cache > 0) {
539                 ndx_gr_cache = num_gr_cache = 0;
540                 free_response(&getgrent_response);
541         }
542
543         return winbindd_request(WINBINDD_SETGRENT, NULL, NULL);
544 }
545
546 /* Close "file pointer" for ntdom group database */
547
548 NSS_STATUS
549 _nss_winbind_endgrent(void)
550 {
551 #ifdef DEBUG_NSS
552         fprintf(stderr, "[%5d]: endgrent\n", getpid());
553 #endif
554
555         if (num_gr_cache > 0) {
556                 ndx_gr_cache = num_gr_cache = 0;
557                 free_response(&getgrent_response);
558         }
559
560         return winbindd_request(WINBINDD_ENDGRENT, NULL, NULL);
561 }
562
563 /* Get next entry from ntdom group database */
564
565 #define MAX_GETGRENT_USERS 250
566
567 NSS_STATUS
568 _nss_winbind_getgrent_r(struct group *result,
569                         char *buffer, size_t buflen, int *errnop)
570 {
571         NSS_STATUS ret;
572         static struct winbindd_request request;
573         static int called_again;
574
575 #ifdef DEBUG_NSS
576         fprintf(stderr, "[%5d]: getgrent\n", getpid());
577 #endif
578
579         /* Return an entry from the cache if we have one, or if we are
580            called again because we exceeded our static buffer.  */
581
582         if ((ndx_gr_cache < num_gr_cache) || called_again) {
583                 goto return_result;
584         }
585
586         /* Else call winbindd to get a bunch of entries */
587         
588         if (num_gr_cache > 0) {
589                 free_response(&getgrent_response);
590         }
591
592         ZERO_STRUCT(request);
593         ZERO_STRUCT(getgrent_response);
594
595         request.data.num_entries = MAX_GETGRENT_USERS;
596
597         ret = winbindd_request(WINBINDD_GETGRENT, &request, 
598                                &getgrent_response);
599
600         if (ret == NSS_STATUS_SUCCESS) {
601                 struct winbindd_gr *gr_cache;
602                 int mem_ofs;
603
604                 /* Fill cache */
605
606                 ndx_gr_cache = 0;
607                 num_gr_cache = getgrent_response.data.num_entries;
608
609                 /* Return a result */
610
611         return_result:
612
613                 gr_cache = getgrent_response.extra_data;
614
615                 /* Check data is valid */
616
617                 if (gr_cache == NULL) {
618                         return NSS_STATUS_NOTFOUND;
619                 }
620
621                 /* Fill group membership.  The offset into the extra data
622                    for the group membership is the reported offset plus the
623                    size of all the winbindd_gr records returned. */
624
625                 mem_ofs = gr_cache[ndx_gr_cache].gr_mem_ofs +
626                         num_gr_cache * sizeof(struct winbindd_gr);
627
628                 ret = fill_grent(result, &gr_cache[ndx_gr_cache],
629                                  ((char *)getgrent_response.extra_data)+mem_ofs,
630                                  &buffer, &buflen);
631                 
632                 /* Out of memory - try again */
633
634                 if (ret == NSS_STATUS_TRYAGAIN) {
635                         called_again = True;
636                         *errnop = errno = ERANGE;
637                         return ret;
638                 }
639
640                 *errnop = 0;
641                 called_again = False;
642                 ndx_gr_cache++;
643
644                 /* If we've finished with this lot of results free cache */
645
646                 if (ndx_gr_cache == num_gr_cache) {
647                         ndx_gr_cache = num_gr_cache = 0;
648                         free_response(&getgrent_response);
649                 }
650         }
651
652         return ret;
653 }
654
655 /* Return group struct from group name */
656
657 NSS_STATUS
658 _nss_winbind_getgrnam_r(const char *name,
659                         struct group *result, char *buffer,
660                         size_t buflen, int *errnop)
661 {
662         NSS_STATUS ret;
663         static struct winbindd_response response;
664         struct winbindd_request request;
665         static int keep_response;
666         
667 #ifdef DEBUG_NSS
668         fprintf(stderr, "[%5d]: getgrnam %s\n", getpid(), name);
669 #endif
670
671         /* If our static buffer needs to be expanded we are called again */
672         
673         if (!keep_response) {
674
675                 /* Call for the first time */
676
677                 ZERO_STRUCT(request);
678                 ZERO_STRUCT(response);
679
680                 strncpy(request.data.groupname, name, 
681                         sizeof(request.data.groupname));
682                 request.data.groupname
683                         [sizeof(request.data.groupname) - 1] = '\0';
684
685                 ret = winbindd_request(WINBINDD_GETGRNAM_FROM_GROUP, 
686                                        &request, &response);
687
688                 if (ret == NSS_STATUS_SUCCESS) {
689                         ret = fill_grent(result, &response.data.gr, 
690                                          response.extra_data,
691                                          &buffer, &buflen);
692
693                         if (ret == NSS_STATUS_TRYAGAIN) {
694                                 keep_response = True;
695                                 *errnop = errno = ERANGE;
696                                 return ret;
697                         }
698                 }
699
700         } else {
701                 
702                 /* We've been called again */
703                 
704                 ret = fill_grent(result, &response.data.gr, 
705                                  response.extra_data, &buffer, &buflen);
706                 
707                 if (ret == NSS_STATUS_TRYAGAIN) {
708                         keep_response = True;
709                         *errnop = errno = ERANGE;
710                         return ret;
711                 }
712
713                 keep_response = False;
714                 *errnop = 0;
715         }
716
717         free_response(&response);
718         return ret;
719 }
720
721 /* Return group struct from gid */
722
723 NSS_STATUS
724 _nss_winbind_getgrgid_r(gid_t gid,
725                         struct group *result, char *buffer,
726                         size_t buflen, int *errnop)
727 {
728         NSS_STATUS ret;
729         static struct winbindd_response response;
730         struct winbindd_request request;
731         static int keep_response;
732
733 #ifdef DEBUG_NSS
734         fprintf(stderr, "[%5d]: getgrgid %d\n", getpid(), gid);
735 #endif
736
737         /* If our static buffer needs to be expanded we are called again */
738
739         if (!keep_response) {
740
741                 /* Call for the first time */
742
743                 ZERO_STRUCT(request);
744                 ZERO_STRUCT(response);
745
746                 request.data.gid = gid;
747
748                 ret = winbindd_request(WINBINDD_GETGRNAM_FROM_GID, &request, 
749                                        &response);
750
751                 if (ret == NSS_STATUS_SUCCESS) {
752
753                         ret = fill_grent(result, &response.data.gr, 
754                                          response.extra_data, 
755                                          &buffer, &buflen);
756
757                         if (ret == NSS_STATUS_TRYAGAIN) {
758                                 keep_response = True;
759                                 *errnop = errno = ERANGE;
760                                 return ret;
761                         }
762                 }
763
764         } else {
765
766                 /* We've been called again */
767
768                 ret = fill_grent(result, &response.data.gr, 
769                                  response.extra_data, &buffer, &buflen);
770
771                 if (ret == NSS_STATUS_TRYAGAIN) {
772                         keep_response = True;
773                         *errnop = errno = ERANGE;
774                         return ret;
775                 }
776
777                 keep_response = False;
778                 *errnop = 0;
779         }
780
781         free_response(&response);
782         return ret;
783 }
784
785 /* Initialise supplementary groups */
786
787 NSS_STATUS
788 _nss_winbind_initgroups(char *user, gid_t group, long int *start,
789                         long int *size, gid_t *groups, long int limit,
790                         int *errnop)
791 {
792         NSS_STATUS ret;
793         struct winbindd_request request;
794         struct winbindd_response response;
795         int i;
796
797 #ifdef DEBUG_NSS
798         fprintf(stderr, "[%5d]: initgroups %s (%d)\n", getpid(),
799                 user, group);
800 #endif
801
802         ZERO_STRUCT(request);
803         ZERO_STRUCT(response);
804
805         strncpy(request.data.username, user,
806                 sizeof(request.data.username) - 1);
807
808         ret = winbindd_request(WINBINDD_GETGROUPS, &request, &response);
809
810         if (ret == NSS_STATUS_SUCCESS) {
811                 int num_gids = response.data.num_entries;
812                 gid_t *gid_list = (gid_t *)response.extra_data;
813
814                 /* Copy group list to client */
815
816                 for (i = 0; i < num_gids; i++) {
817
818                         /* Skip primary group */
819
820                         if (gid_list[i] == group) continue;
821
822                         /* Add to buffer */
823
824                         if (*start == *size && limit <= 0) {
825                                 groups = realloc(
826                                         groups, 2 * (*size) * sizeof(*groups));
827                                 if (!groups) goto done;
828                                 *size *= 2;
829                         }
830
831                         groups[*start] = gid_list[i];
832                         *start += 1;
833
834                         /* Filled buffer? */
835
836                         if (*start == limit) goto done;
837                 }
838         }
839         
840         /* Back to your regularly scheduled programming */
841
842  done:
843         return ret;
844 }