Initial import
[samba] / source / nsswitch / winbind_nss_solaris.c
1 /*
2   Solaris NSS wrapper for winbind 
3   - Shirish Kalele 2000
4   
5   Based on Luke Howard's ldap_nss module for Solaris 
6   */
7
8 /*
9   Copyright (C) 1997-2003 Luke Howard.
10   This file is part of the nss_ldap library.
11
12   The nss_ldap library is free software; you can redistribute it and/or
13   modify it under the terms of the GNU Library General Public License as
14   published by the Free Software Foundation; either version 2 of the
15   License, or (at your option) any later version.
16
17   The nss_ldap library is distributed in the hope that it will be useful,
18   but WITHOUT ANY WARRANTY; without even the implied warranty of
19   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20   Library General Public License for more details.
21
22   You should have received a copy of the GNU Library General Public
23   License along with the nss_ldap library; see the file COPYING.LIB.  If not,
24   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25   Boston, MA 02111-1307, USA.
26 */
27
28 #undef DEVELOPER
29
30 #include <stdlib.h>
31 #include <sys/types.h>
32 #include <sys/param.h>
33 #include <string.h>
34 #include <pwd.h>
35 #include "includes.h"
36 #include <syslog.h>
37 #if !defined(HPUX)
38 #include <sys/syslog.h>
39 #endif /*hpux*/
40 #include "winbind_nss_config.h"
41
42 #if defined(HAVE_NSS_COMMON_H) || defined(HPUX) 
43
44 #undef NSS_DEBUG
45
46 #ifdef NSS_DEBUG
47 #define NSS_DEBUG(str) syslog(LOG_DEBUG, "nss_winbind: %s", str);
48 #else
49 #define NSS_DEBUG(str) ;
50 #endif
51
52 #define NSS_ARGS(args) ((nss_XbyY_args_t *)args)
53
54 #ifdef HPUX
55
56 /*
57  * HP-UX 11 has no definiton of the nss_groupsbymem structure.   This
58  * definition is taken from the nss_ldap project at:
59  *  http://www.padl.com/OSS/nss_ldap.html
60  */
61
62 struct nss_groupsbymem {
63        const char *username;
64        gid_t *gid_array;
65        int maxgids;
66        int force_slow_way;
67        int (*str2ent)(const char *instr, int instr_len, void *ent, 
68                       char *buffer, int buflen);
69        nss_status_t (*process_cstr)(const char *instr, int instr_len, 
70                                     struct nss_groupsbymem *);
71        int numgids;
72 };
73
74 #endif /* HPUX */
75
76 #define make_pwent_str(dest, src)                                       \
77 {                                                                       \
78   if((dest = get_static(buffer, buflen, strlen(src)+1)) == NULL)        \
79     {                                                                   \
80       *errnop = ERANGE;                                                 \
81       NSS_DEBUG("ERANGE error");                                        \
82       return NSS_STATUS_TRYAGAIN;                                       \
83     }                                                                   \
84   strcpy(dest, src);                                                    \
85 }
86
87 static NSS_STATUS _nss_winbind_setpwent_solwrap (nss_backend_t* be, void* args)
88 {
89         NSS_DEBUG("_nss_winbind_setpwent_solwrap");
90         return _nss_winbind_setpwent();
91 }
92
93 static NSS_STATUS
94 _nss_winbind_endpwent_solwrap (nss_backend_t * be, void *args)
95 {
96         NSS_DEBUG("_nss_winbind_endpwent_solwrap");
97         return _nss_winbind_endpwent();
98 }
99
100 static NSS_STATUS
101 _nss_winbind_getpwent_solwrap (nss_backend_t* be, void *args)
102 {
103         NSS_STATUS ret;
104         char* buffer = NSS_ARGS(args)->buf.buffer;
105         int buflen = NSS_ARGS(args)->buf.buflen;
106         struct passwd* result = (struct passwd*) NSS_ARGS(args)->buf.result;
107         int* errnop = &NSS_ARGS(args)->erange;
108         char logmsg[80];
109
110         ret = _nss_winbind_getpwent_r(result, buffer, 
111                                       buflen, errnop);
112
113         if(ret == NSS_STATUS_SUCCESS)
114                 {
115                         snprintf(logmsg, 79, "_nss_winbind_getpwent_solwrap: Returning user: %s\n",
116                                  result->pw_name);
117                         NSS_DEBUG(logmsg);
118                         NSS_ARGS(args)->returnval = (void*) result;
119                 } else {
120                         snprintf(logmsg, 79, "_nss_winbind_getpwent_solwrap: Returning error: %d.\n",ret);
121                         NSS_DEBUG(logmsg);
122                 }
123     
124         return ret;
125 }
126
127 static NSS_STATUS
128 _nss_winbind_getpwnam_solwrap (nss_backend_t* be, void* args)
129 {
130         NSS_STATUS ret;
131         struct passwd* result = (struct passwd*) NSS_ARGS(args)->buf.result;
132
133         NSS_DEBUG("_nss_winbind_getpwnam_solwrap");
134
135         ret = _nss_winbind_getpwnam_r (NSS_ARGS(args)->key.name,
136                                                 result,
137                                                 NSS_ARGS(args)->buf.buffer,
138                                                 NSS_ARGS(args)->buf.buflen,
139                                                 &NSS_ARGS(args)->erange);
140         if(ret == NSS_STATUS_SUCCESS)
141                 NSS_ARGS(args)->returnval = (void*) result;
142   
143         return ret;
144 }
145
146 static NSS_STATUS
147 _nss_winbind_getpwuid_solwrap(nss_backend_t* be, void* args)
148 {
149         NSS_STATUS ret;
150         struct passwd* result = (struct passwd*) NSS_ARGS(args)->buf.result;
151   
152         NSS_DEBUG("_nss_winbind_getpwuid_solwrap");
153         ret = _nss_winbind_getpwuid_r (NSS_ARGS(args)->key.uid,
154                                        result,
155                                        NSS_ARGS(args)->buf.buffer,
156                                        NSS_ARGS(args)->buf.buflen,
157                                        &NSS_ARGS(args)->erange);
158         if(ret == NSS_STATUS_SUCCESS)
159                 NSS_ARGS(args)->returnval = (void*) result;
160   
161         return ret;
162 }
163
164 static NSS_STATUS _nss_winbind_passwd_destr (nss_backend_t * be, void *args)
165 {
166         SAFE_FREE(be);
167         NSS_DEBUG("_nss_winbind_passwd_destr");
168         return NSS_STATUS_SUCCESS;
169 }
170
171 static nss_backend_op_t passwd_ops[] =
172 {
173         _nss_winbind_passwd_destr,
174         _nss_winbind_endpwent_solwrap,          /* NSS_DBOP_ENDENT */
175         _nss_winbind_setpwent_solwrap,          /* NSS_DBOP_SETENT */
176         _nss_winbind_getpwent_solwrap,          /* NSS_DBOP_GETENT */
177         _nss_winbind_getpwnam_solwrap,          /* NSS_DBOP_PASSWD_BYNAME */
178         _nss_winbind_getpwuid_solwrap           /* NSS_DBOP_PASSWD_BYUID */
179 };
180
181 nss_backend_t*
182 _nss_winbind_passwd_constr (const char* db_name,
183                             const char* src_name,
184                             const char* cfg_args)
185 {
186         nss_backend_t *be;
187   
188         if(!(be = (nss_backend_t*) malloc(sizeof(nss_backend_t))) )
189                 return NULL;
190
191         be->ops = passwd_ops;
192         be->n_ops = sizeof(passwd_ops) / sizeof(nss_backend_op_t);
193
194         NSS_DEBUG("Initialized nss_winbind passwd backend");
195         return be;
196 }
197
198 /*****************************************************************
199  GROUP database backend
200  *****************************************************************/
201
202 static NSS_STATUS _nss_winbind_setgrent_solwrap (nss_backend_t* be, void* args)
203 {
204         NSS_DEBUG("_nss_winbind_setgrent_solwrap");
205         return _nss_winbind_setgrent();
206 }
207
208 static NSS_STATUS
209 _nss_winbind_endgrent_solwrap (nss_backend_t * be, void *args)
210 {
211         NSS_DEBUG("_nss_winbind_endgrent_solwrap");
212         return _nss_winbind_endgrent();
213 }
214
215 static NSS_STATUS
216 _nss_winbind_getgrent_solwrap(nss_backend_t* be, void* args)
217 {
218         NSS_STATUS ret;
219         char* buffer = NSS_ARGS(args)->buf.buffer;
220         int buflen = NSS_ARGS(args)->buf.buflen;
221         struct group* result = (struct group*) NSS_ARGS(args)->buf.result;
222         int* errnop = &NSS_ARGS(args)->erange;
223         char logmsg[80];
224
225         ret = _nss_winbind_getgrent_r(result, buffer, 
226                                       buflen, errnop);
227
228         if(ret == NSS_STATUS_SUCCESS)
229                 {
230                         snprintf(logmsg, 79, "_nss_winbind_getgrent_solwrap: Returning group: %s\n", result->gr_name);
231                         NSS_DEBUG(logmsg);
232                         NSS_ARGS(args)->returnval = (void*) result;
233                 } else {
234                         snprintf(logmsg, 79, "_nss_winbind_getgrent_solwrap: Returning error: %d.\n", ret);
235                         NSS_DEBUG(logmsg);
236                 }
237
238         return ret;
239         
240 }
241
242 static NSS_STATUS
243 _nss_winbind_getgrnam_solwrap(nss_backend_t* be, void* args)
244 {
245         NSS_STATUS ret;
246         struct group* result = (struct group*) NSS_ARGS(args)->buf.result;
247
248         NSS_DEBUG("_nss_winbind_getgrnam_solwrap");
249         ret = _nss_winbind_getgrnam_r(NSS_ARGS(args)->key.name,
250                                       result,
251                                       NSS_ARGS(args)->buf.buffer,
252                                       NSS_ARGS(args)->buf.buflen,
253                                       &NSS_ARGS(args)->erange);
254
255         if(ret == NSS_STATUS_SUCCESS)
256                 NSS_ARGS(args)->returnval = (void*) result;
257   
258         return ret;
259 }
260   
261 static NSS_STATUS
262 _nss_winbind_getgrgid_solwrap(nss_backend_t* be, void* args)
263 {
264         NSS_STATUS ret;
265         struct group* result = (struct group*) NSS_ARGS(args)->buf.result;
266
267         NSS_DEBUG("_nss_winbind_getgrgid_solwrap");
268         ret = _nss_winbind_getgrgid_r (NSS_ARGS(args)->key.gid,
269                                        result,
270                                        NSS_ARGS(args)->buf.buffer,
271                                        NSS_ARGS(args)->buf.buflen,
272                                        &NSS_ARGS(args)->erange);
273
274         if(ret == NSS_STATUS_SUCCESS)
275                 NSS_ARGS(args)->returnval = (void*) result;
276
277         return ret;
278 }
279
280 static NSS_STATUS
281 _nss_winbind_getgroupsbymember_solwrap(nss_backend_t* be, void* args)
282 {
283         int errnop;
284         struct nss_groupsbymem *gmem = (struct nss_groupsbymem *)args;
285
286         NSS_DEBUG("_nss_winbind_getgroupsbymember");
287
288         _nss_winbind_initgroups_dyn(gmem->username,
289                 gmem->gid_array[0], /* Primary Group */
290                 &gmem->numgids,
291                 &gmem->maxgids,
292                 &gmem->gid_array,
293                 gmem->maxgids,
294                 &errnop);
295
296         /*
297          * If the maximum number of gids have been found, return
298          * SUCCESS so the switch engine will stop searching. Otherwise
299          * return NOTFOUND so nsswitch will continue to get groups
300          * from the remaining database backends specified in the
301          * nsswitch.conf file.
302          */
303         return (gmem->numgids == gmem->maxgids ? NSS_STATUS_SUCCESS : NSS_STATUS_NOTFOUND);
304 }
305
306 static NSS_STATUS
307 _nss_winbind_group_destr (nss_backend_t* be, void* args)
308 {
309         SAFE_FREE(be);
310         NSS_DEBUG("_nss_winbind_group_destr");
311         return NSS_STATUS_SUCCESS;
312 }
313
314 static nss_backend_op_t group_ops[] = 
315 {
316         _nss_winbind_group_destr,
317         _nss_winbind_endgrent_solwrap,
318         _nss_winbind_setgrent_solwrap,
319         _nss_winbind_getgrent_solwrap,
320         _nss_winbind_getgrnam_solwrap,
321         _nss_winbind_getgrgid_solwrap,
322         _nss_winbind_getgroupsbymember_solwrap
323 }; 
324
325 nss_backend_t*
326 _nss_winbind_group_constr (const char* db_name,
327                            const char* src_name,
328                            const char* cfg_args)
329 {
330         nss_backend_t* be;
331
332         if(!(be = (nss_backend_t*) malloc(sizeof(nss_backend_t))) )
333                 return NULL;
334
335         be->ops = group_ops;
336         be->n_ops = sizeof(group_ops) / sizeof(nss_backend_op_t);
337   
338         NSS_DEBUG("Initialized nss_winbind group backend");
339         return be;
340 }
341
342 /*****************************************************************
343  hosts and ipnodes backend
344  *****************************************************************/
345 #if defined(SUNOS5)     /* not compatible with HP-UX */
346
347 /* this parser is shared between get*byname and get*byaddr, as key type
348    in request is stored in different locations, I had to provide the
349    address family as an argument, caller must free the winbind response. */
350
351 static NSS_STATUS
352 parse_response(int af, nss_XbyY_args_t* argp, struct winbindd_response *response)
353 {
354         struct hostent *he = (struct hostent *)argp->buf.result;
355         char *buffer = argp->buf.buffer;
356         int buflen =  argp->buf.buflen;
357         NSS_STATUS ret;
358
359         char *p, *data;
360         int addrcount = 0;
361         int len = 0;
362         struct in_addr *addrp;
363         struct in6_addr *addrp6;
364         int i;
365
366         /* response is tab separated list of ip addresses with hostname
367            and newline at the end. so at first we will strip newline
368            then construct list of addresses for hostent.
369         */
370         p = strchr(response->data.winsresp, '\n');
371         if(p) *p = '\0';
372         else {/* it must be broken */
373                 argp->h_errno = NO_DATA;
374                 return NSS_STATUS_UNAVAIL;
375         }
376
377         for(; p != response->data.winsresp; p--) {
378                 if(*p == '\t') addrcount++;
379         }
380
381         if(addrcount == 0) {/* it must be broken */
382                 argp->h_errno = NO_DATA;
383                 return NSS_STATUS_UNAVAIL;
384         }
385
386         /* allocate space for addresses and h_addr_list */
387         he->h_addrtype = af;
388         if( he->h_addrtype == AF_INET) {
389                 he->h_length =  sizeof(struct in_addr);
390                 addrp = (struct in_addr *)ROUND_DOWN(buffer + buflen,
391                                                 sizeof(struct in_addr));
392                 addrp -= addrcount;
393                 he->h_addr_list = (char **)ROUND_DOWN(addrp, sizeof (char*));
394                 he->h_addr_list -= addrcount+1;
395         } else {
396                 he->h_length = sizeof(struct in6_addr);
397                 addrp6 = (struct in6_addr *)ROUND_DOWN(buffer + buflen,
398                                                 sizeof(struct in6_addr));
399                 addrp6 -= addrcount;
400                 he->h_addr_list = (char **)ROUND_DOWN(addrp6, sizeof (char*));
401                 he->h_addr_list -= addrcount+1;
402         }
403
404         /* buffer too small?! */
405         if((char *)he->h_addr_list < buffer ) {
406                 argp->erange = 1;
407                 return NSS_STR_PARSE_ERANGE;
408         }
409         
410         data = response->data.winsresp;
411         for( i = 0; i < addrcount; i++) {
412                 p = strchr(data, '\t');
413                 if(p == NULL) break; /* just in case... */
414
415                 *p = '\0'; /* terminate the string */
416                 if(he->h_addrtype == AF_INET) {
417                   he->h_addr_list[i] = (char *)&addrp[i];
418                   if ((addrp[i].s_addr = inet_addr(data)) == -1) {
419                     argp->erange = 1;
420                     return NSS_STR_PARSE_ERANGE;
421                   }
422                 } else {
423                   he->h_addr_list[i] = (char *)&addrp6[i];
424                   if (strchr(data, ':') != 0) {
425                         if (inet_pton(AF_INET6, data, &addrp6[i]) != 1) {
426                           argp->erange = 1;
427                           return NSS_STR_PARSE_ERANGE;
428                         }
429                   } else {
430                         struct in_addr in4;
431                         if ((in4.s_addr = inet_addr(data)) == -1) {
432                           argp->erange = 1;
433                           return NSS_STR_PARSE_ERANGE;
434                         }
435                         IN6_INADDR_TO_V4MAPPED(&in4, &addrp6[i]);
436                   }
437                 }
438                 data = p+1;
439         }
440
441         he->h_addr_list[i] = (char *)NULL;
442
443         len = strlen(data);
444         if(len > he->h_addr_list - (char**)argp->buf.buffer) {
445                 argp->erange = 1;
446                 return NSS_STR_PARSE_ERANGE;
447         }
448
449         /* this is a bit overkill to use _nss_netdb_aliases here since
450            there seems to be no aliases but it will create all data for us */
451         he->h_aliases = _nss_netdb_aliases(data, len, buffer,
452                                 ((char*) he->h_addr_list) - buffer);
453         if(he->h_aliases == NULL) {
454             argp->erange = 1;
455             ret = NSS_STR_PARSE_ERANGE;
456         } else {
457             he->h_name = he->h_aliases[0];
458             he->h_aliases++;
459             ret = NSS_STR_PARSE_SUCCESS;
460         }
461
462         argp->returnval = (void*)he;
463         return ret;
464 }
465
466 static NSS_STATUS
467 _nss_winbind_ipnodes_getbyname(nss_backend_t* be, void *args)
468 {
469         nss_XbyY_args_t *argp = (nss_XbyY_args_t*) args;
470         struct winbindd_response response;
471         struct winbindd_request request;
472         NSS_STATUS ret;
473         int af;
474
475         ZERO_STRUCT(response);
476         ZERO_STRUCT(request);
477
478         /* I assume there that AI_ADDRCONFIG cases are handled in nss
479            frontend code, at least it seems done so in solaris...
480
481            we will give NO_DATA for pure IPv6; IPv4 will be returned for
482            AF_INET or for AF_INET6 and AI_ALL|AI_V4MAPPED we have to map
483            IPv4 to IPv6.
484          */
485 #ifdef HAVE_NSS_XBYY_KEY_IPNODE
486         af = argp->key.ipnode.af_family;
487         if(af == AF_INET6 && argp->key.ipnode.flags == 0) {
488                 argp->h_errno = NO_DATA;
489                 return NSS_STATUS_UNAVAIL;
490         }
491 #else
492         /* I'm not that sure if this is correct, but... */
493         af = AF_INET6;
494 #endif
495
496         strncpy(request.data.winsreq, argp->key.name, strlen(argp->key.name)) ;
497
498         if( (ret = winbindd_request_response(WINBINDD_WINS_BYNAME, &request, &response))
499                 == NSS_STATUS_SUCCESS ) {
500           ret = parse_response(af, argp, &response);
501         }
502
503         free_response(&response);
504         return ret;
505 }
506
507 static NSS_STATUS
508 _nss_winbind_hosts_getbyname(nss_backend_t* be, void *args)
509 {
510         nss_XbyY_args_t *argp = (nss_XbyY_args_t*) args;
511         struct winbindd_response response;
512         struct winbindd_request request;
513         NSS_STATUS ret;
514
515         ZERO_STRUCT(response);
516         ZERO_STRUCT(request);
517         
518         strncpy(request.data.winsreq, argp->key.name, strlen(argp->key.name));
519
520         if( (ret = winbindd_request_response(WINBINDD_WINS_BYNAME, &request, &response))
521                 == NSS_STATUS_SUCCESS ) {
522           ret = parse_response(AF_INET, argp, &response);
523         }
524
525         free_response(&response);
526         return ret;
527 }
528
529 static NSS_STATUS
530 _nss_winbind_hosts_getbyaddr(nss_backend_t* be, void *args)
531 {
532         NSS_STATUS ret;
533         struct winbindd_response response;
534         struct winbindd_request request;
535         nss_XbyY_args_t *argp = (nss_XbyY_args_t *)args;
536         const char *p;
537
538         ZERO_STRUCT(response);
539         ZERO_STRUCT(request);
540
541         /* winbindd currently does not resolve IPv6 */
542         if(argp->key.hostaddr.type == AF_INET6) {
543                 argp->h_errno = NO_DATA;
544                 return NSS_STATUS_UNAVAIL;
545         }
546
547         p = inet_ntop(argp->key.hostaddr.type, argp->key.hostaddr.addr,
548                         request.data.winsreq, INET6_ADDRSTRLEN);
549
550         ret = winbindd_request_response(WINBINDD_WINS_BYIP, &request, &response);
551
552         if( ret == NSS_STATUS_SUCCESS) {
553           parse_response(argp->key.hostaddr.type, argp, &response);
554         }
555         free_response(&response);
556         return ret;
557 }
558
559 /* winbind does not provide setent, getent, endent for wins */
560 static NSS_STATUS
561 _nss_winbind_common_endent(nss_backend_t* be, void *args)
562 {
563         return (NSS_STATUS_UNAVAIL);
564 }
565
566 static NSS_STATUS
567 _nss_winbind_common_setent(nss_backend_t* be, void *args)
568 {
569         return (NSS_STATUS_UNAVAIL);
570 }
571
572 static NSS_STATUS
573 _nss_winbind_common_getent(nss_backend_t* be, void *args)
574 {
575         return (NSS_STATUS_UNAVAIL);
576 }
577
578 static nss_backend_t*
579 _nss_winbind_common_constr (nss_backend_op_t ops[], int n_ops)
580 {
581         nss_backend_t* be;
582
583         if(!(be = (nss_backend_t*) malloc(sizeof(nss_backend_t))) )
584         return NULL;
585
586         be->ops = ops;
587         be->n_ops = n_ops;
588
589         return be;
590 }
591
592 static NSS_STATUS
593 _nss_winbind_common_destr (nss_backend_t* be, void* args)
594 {
595         SAFE_FREE(be);
596         return NSS_STATUS_SUCCESS;
597 }
598
599 static nss_backend_op_t ipnodes_ops[] = {
600         _nss_winbind_common_destr,
601         _nss_winbind_common_endent,
602         _nss_winbind_common_setent,
603         _nss_winbind_common_getent,
604         _nss_winbind_ipnodes_getbyname,
605         _nss_winbind_hosts_getbyaddr,
606 };
607
608 nss_backend_t *
609 _nss_winbind_ipnodes_constr(dummy1, dummy2, dummy3)
610         const char      *dummy1, *dummy2, *dummy3;
611 {
612         return (_nss_winbind_common_constr(ipnodes_ops,
613                 sizeof (ipnodes_ops) / sizeof (ipnodes_ops[0])));
614 }
615
616 static nss_backend_op_t host_ops[] = {
617         _nss_winbind_common_destr,
618         _nss_winbind_common_endent,
619         _nss_winbind_common_setent,
620         _nss_winbind_common_getent,
621         _nss_winbind_hosts_getbyname,
622         _nss_winbind_hosts_getbyaddr,
623 };
624
625 nss_backend_t *
626 _nss_winbind_hosts_constr(dummy1, dummy2, dummy3)
627         const char      *dummy1, *dummy2, *dummy3;
628 {
629         return (_nss_winbind_common_constr(host_ops,
630                 sizeof (host_ops) / sizeof (host_ops[0])));
631 }
632
633 #endif  /* defined(SUNOS5) */
634 #endif  /* defined(HAVE_NSS_COMMON_H) || defined(HPUX) */