- changed smb_getpwnam() to use winbind style usernames
[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 void init_request(struct winbindd_request *req,int rq_type);
30 int write_sock(void *buffer, int count);
31 int read_reply(struct winbindd_response *response);
32
33
34 /* Allocate some space from the nss static buffer.  The buffer and buflen
35    are the pointers passed in by the C library to the _nss_ntdom_*
36    functions. */
37
38 static char *get_static(char **buffer, int *buflen, int len)
39 {
40     char *result;
41
42     /* Error check.  We return false if things aren't set up right, or
43        there isn't enough buffer space left. */
44
45     if ((buffer == NULL) || (buflen == NULL) || (*buflen < len)) {
46         return NULL;
47     }
48
49     /* Return an index into the static buffer */
50
51     result = *buffer;
52     *buffer += len;
53     *buflen -= len;
54
55     return result;
56 }
57
58 /* I've copied the strtok() replacement function next_token() from
59    lib/util_str.c as I really don't want to have to link in any other
60    objects if I can possibly avoid it. */
61
62 #ifdef strchr /* Aargh! This points at multibyte_strchr(). )-: */
63 #undef strchr
64 #endif
65
66 static char *last_ptr = NULL;
67
68 BOOL next_token(char **ptr, char *buff, char *sep, size_t bufsize)
69 {
70     char *s;
71     BOOL quoted;
72     size_t len=1;
73     
74     if (!ptr) ptr = &last_ptr;
75     if (!ptr) return(False);
76     
77     s = *ptr;
78     
79     /* default to simple separators */
80     if (!sep) sep = " \t\n\r";
81     
82     /* find the first non sep char */
83     while(*s && strchr(sep,*s)) s++;
84     
85     /* nothing left? */
86     if (! *s) return(False);
87     
88     /* copy over the token */
89     for (quoted = False; 
90          len < bufsize && *s && (quoted || !strchr(sep,*s)); 
91          s++) {
92
93         if (*s == '\"') {
94             quoted = !quoted;
95         } else {
96             len++;
97             *buff++ = *s;
98         }
99     }
100     
101     *ptr = (*s) ? s+1 : s;  
102     *buff = 0;
103     last_ptr = *ptr;
104   
105     return(True);
106 }
107
108
109 /* handle simple types of requests */
110 static enum nss_status generic_request(int req_type, 
111                                        struct winbindd_request *request,
112                                        struct winbindd_response *response)
113 {
114         struct winbindd_request lrequest;
115         struct winbindd_response lresponse;
116
117         if (!response) response = &lresponse;
118         if (!request) request = &lrequest;
119         
120         /* Fill in request and send down pipe */
121         init_request(request, req_type);
122         
123         if (write_sock(request, sizeof(*request)) == -1) {
124                 return NSS_STATUS_UNAVAIL;
125         }
126         
127         /* Wait for reply */
128         if (read_reply(response) == -1) {
129                 return NSS_STATUS_UNAVAIL;
130         }
131
132         /* Copy reply data from socket */
133         if (response->result != WINBINDD_OK) {
134                 return NSS_STATUS_NOTFOUND;
135         }
136         
137         return NSS_STATUS_SUCCESS;
138 }
139
140 /* Fill a pwent structure from a winbindd_response structure.  We use
141    the static data passed to us by libc to put strings and stuff in.
142    Return errno = ERANGE and NSS_STATUS_TRYAGAIN if we run out of
143    memory. */
144
145 static enum nss_status fill_pwent(struct passwd *result,
146                                   struct winbindd_response *response,
147                                   char **buffer, int *buflen, int *errnop)
148 {
149     struct winbindd_pw *pw = &response->data.pw;
150
151     /* User name */
152
153     if ((result->pw_name = 
154          get_static(buffer, buflen, strlen(pw->pw_name) + 1)) == NULL) {
155
156         /* Out of memory */
157
158         *errnop = ERANGE;
159         return NSS_STATUS_TRYAGAIN;
160     }
161
162     strcpy(result->pw_name, pw->pw_name);
163
164     /* Password */
165
166     if ((result->pw_passwd = 
167          get_static(buffer, buflen, strlen(pw->pw_passwd) + 1)) == NULL) {
168
169         /* Out of memory */
170
171         *errnop = ERANGE;
172         return NSS_STATUS_TRYAGAIN;
173     }
174
175     strcpy(result->pw_passwd, pw->pw_passwd);
176         
177     /* [ug]id */
178
179     result->pw_uid = pw->pw_uid;
180     result->pw_gid = pw->pw_gid;
181
182     /* GECOS */
183
184     if ((result->pw_gecos = 
185          get_static(buffer, buflen, strlen(pw->pw_gecos) + 1)) == NULL) {
186
187         /* Out of memory */
188
189         *errnop = ERANGE;
190         return NSS_STATUS_TRYAGAIN;
191     }
192
193     strcpy(result->pw_gecos, pw->pw_gecos);
194
195     /* Home directory */
196
197     if ((result->pw_dir = 
198          get_static(buffer, buflen, strlen(pw->pw_dir) + 1)) == NULL) {
199
200         /* Out of memory */
201
202         *errnop = ERANGE;
203         return NSS_STATUS_TRYAGAIN;
204     }
205
206     strcpy(result->pw_dir, pw->pw_dir);
207
208     /* Logon shell */
209
210     if ((result->pw_shell = 
211          get_static(buffer, buflen, strlen(pw->pw_shell) + 1)) == NULL) {
212
213         /* Out of memory */
214
215         *errnop = ERANGE;
216         return NSS_STATUS_TRYAGAIN;
217     }
218
219     strcpy(result->pw_shell, pw->pw_shell);
220
221     return NSS_STATUS_SUCCESS;
222 }
223
224 /* Fill a grent structure from a winbindd_response structure.  We use
225    the static data passed to us by libc to put strings and stuff in.
226    Return errno = ERANGE and NSS_STATUS_TRYAGAIN if we run out of
227    memory. */
228
229 static int fill_grent(struct group *result, 
230                       struct winbindd_response *response,
231                       char **buffer, int *buflen, int *errnop)
232 {
233     struct winbindd_gr *gr = &response->data.gr;
234     fstring name;
235     int i;
236
237     /* Group name */
238
239     if ((result->gr_name =
240          get_static(buffer, buflen, strlen(gr->gr_name) + 1)) == NULL) {
241
242         /* Out of memory */
243
244         *errnop = ERANGE;
245         return NSS_STATUS_TRYAGAIN;
246     }
247
248     strcpy(result->gr_name, gr->gr_name);
249
250     /* Password */
251
252     if ((result->gr_passwd =
253          get_static(buffer, buflen, strlen(gr->gr_passwd) + 1)) == NULL) {
254
255         /* Out of memory */
256
257         *errnop = ERANGE;
258         return NSS_STATUS_TRYAGAIN;
259     }
260
261     strcpy(result->gr_passwd, gr->gr_passwd);
262
263     /* gid */
264
265     result->gr_gid = gr->gr_gid;
266
267     /* Group membership */
268
269     if ((gr->num_gr_mem < 0) || !response->extra_data) {
270         gr->num_gr_mem = 0;
271     }
272
273     if ((result->gr_mem = 
274          (char **)get_static(buffer, buflen, (gr->num_gr_mem + 1) * 
275                              sizeof(char *))) == NULL) {
276
277         /* Out of memory */
278
279         *errnop = ERANGE;
280         return NSS_STATUS_TRYAGAIN;
281     }
282
283     if (gr->num_gr_mem == 0) {
284
285         /* Group is empty */
286
287         *(result->gr_mem) = NULL;
288         return NSS_STATUS_SUCCESS;
289     }
290
291     /* Start looking at extra data */
292
293     i = 0;
294
295     while(next_token(&response->extra_data, name, ",", sizeof(fstring))) {
296         
297         /* Allocate space for member */
298         
299         if (((result->gr_mem)[i] = 
300              get_static(buffer, buflen, strlen(name) + 1)) == NULL) {
301             
302             /* Out of memory */
303             
304             *errnop = ERANGE;
305             return NSS_STATUS_TRYAGAIN;
306         }        
307         
308         strcpy((result->gr_mem)[i], name);
309         i++;
310     }
311
312     /* Terminate list */
313
314     (result->gr_mem)[i] = NULL;
315     
316     return NSS_STATUS_SUCCESS;
317 }
318
319 /*
320  * NSS user functions
321  */
322
323 /* Rewind "file pointer" to start of ntdom password database */
324
325 enum nss_status
326 _nss_winbind_setpwent(void)
327 {
328         return generic_request(WINBINDD_SETPWENT, NULL, NULL);
329 }
330
331 /* Close ntdom password database "file pointer" */
332
333 enum nss_status
334 _nss_winbind_endpwent(void)
335 {
336         return generic_request(WINBINDD_ENDPWENT, NULL, NULL);
337 }
338
339 /* Fetch the next password entry from ntdom password database */
340
341 enum nss_status
342 _nss_winbind_getpwent_r(struct passwd *result, char *buffer, 
343                       size_t buflen, int *errnop)
344 {
345         enum nss_status ret;
346         struct winbindd_response response;
347
348         ret = generic_request(WINBINDD_GETPWENT, NULL, &response);
349         if (ret != NSS_STATUS_SUCCESS) return ret;
350
351         return fill_pwent(result, &response, &buffer, &buflen, errnop);
352 }
353
354 /* Return passwd struct from uid */
355
356 enum nss_status
357 _nss_winbind_getpwuid_r(uid_t uid, struct passwd *result, char *buffer,
358                       size_t buflen, int *errnop)
359 {
360         enum nss_status ret;
361         struct winbindd_response response;
362         struct winbindd_request request;
363
364         request.data.uid = uid;
365
366         ret = generic_request(WINBINDD_GETPWNAM_FROM_UID, &request, &response);
367         if (ret != NSS_STATUS_SUCCESS) return ret;
368
369         return fill_pwent(result, &response, &buffer, &buflen, errnop);
370 }
371
372 /* Return passwd struct from username */
373
374 enum nss_status
375 _nss_winbind_getpwnam_r(const char *name, struct passwd *result, char *buffer,
376                       size_t buflen, int *errnop)
377 {
378         enum nss_status ret;
379         struct winbindd_response response;
380         struct winbindd_request request;
381
382         strncpy(request.data.username, name, sizeof(request.data.username) - 1);
383         request.data.username[sizeof(request.data.username) - 1] = '\0';
384
385         ret = generic_request(WINBINDD_GETPWNAM_FROM_USER, &request, &response);
386         if (ret != NSS_STATUS_SUCCESS) return ret;
387
388         return fill_pwent(result, &response, &buffer, &buflen, errnop);
389 }
390
391 /*
392  * NSS group functions
393  */
394
395 /* Rewind "file pointer" to start of ntdom group database */
396
397 enum nss_status
398 _nss_winbind_setgrent(void)
399 {
400         return generic_request(WINBINDD_SETGRENT, NULL, NULL);
401 }
402
403 /* Close "file pointer" for ntdom group database */
404
405 enum nss_status
406 _nss_winbind_endgrent(void)
407 {
408         return generic_request(WINBINDD_ENDGRENT, NULL, NULL);
409 }
410
411
412
413 /* Get next entry from ntdom group database */
414
415 enum nss_status
416 _nss_winbind_getgrent_r(struct group *result,
417                       char *buffer, size_t buflen, int *errnop)
418 {
419         enum nss_status ret;
420         struct winbindd_response response;
421
422         ret = generic_request(WINBINDD_GETGRENT, NULL, &response);
423         if (ret != NSS_STATUS_SUCCESS) return ret;
424
425         return fill_grent(result, &response, &buffer, &buflen, errnop);
426 }
427
428 /* Return group struct from group name */
429
430 enum nss_status
431 _nss_winbind_getgrnam_r(const char *name,
432                       struct group *result, char *buffer,
433                       size_t buflen, int *errnop)
434 {
435         enum nss_status ret;
436         struct winbindd_response response;
437         struct winbindd_request request;
438
439         strncpy(request.data.groupname, name, sizeof(request.data.groupname));
440         request.data.groupname[sizeof(request.data.groupname) - 1] = '\0';
441
442         ret = generic_request(WINBINDD_GETGRNAM_FROM_GROUP, &request, &response);
443         if (ret != NSS_STATUS_SUCCESS) return ret;
444
445         return fill_grent(result, &response, &buffer, &buflen, errnop);
446 }
447
448 /* Return group struct from gid */
449
450 enum nss_status
451 _nss_winbind_getgrgid_r(gid_t gid,
452                       struct group *result, char *buffer,
453                       size_t buflen, int *errnop)
454 {
455         enum nss_status ret;
456         struct winbindd_response response;
457         struct winbindd_request request;
458
459         request.data.gid = gid;
460
461         ret = generic_request(WINBINDD_GETGRNAM_FROM_GID, &request, &response);
462         if (ret != NSS_STATUS_SUCCESS) return ret;
463
464         return fill_grent(result, &response, &buffer, &buflen, errnop);
465 }