r23330: always include "winbind_client.h" as first header
[sfrench/samba-autobuild/.git] / 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 "winbind_client.h"
31 #include <stdlib.h>
32 #include <sys/types.h>
33 #include <sys/param.h>
34 #include <string.h>
35 #include <pwd.h>
36 #include "includes.h"
37 #include <syslog.h>
38 #if !defined(HPUX)
39 #include <sys/syslog.h>
40 #endif /*hpux*/
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 = SMB_MALLOC_P(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 = SMB_MALLOC_P(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, sizeof(request.data.winsreq) - 1);
497         request.data.winsreq[sizeof(request.data.winsreq) - 1] = '\0';
498
499         if( (ret = winbindd_request_response(WINBINDD_WINS_BYNAME, &request, &response))
500                 == NSS_STATUS_SUCCESS ) {
501           ret = parse_response(af, argp, &response);
502         }
503
504         free_response(&response);
505         return ret;
506 }
507
508 static NSS_STATUS
509 _nss_winbind_hosts_getbyname(nss_backend_t* be, void *args)
510 {
511         nss_XbyY_args_t *argp = (nss_XbyY_args_t*) args;
512         struct winbindd_response response;
513         struct winbindd_request request;
514         NSS_STATUS ret;
515
516         ZERO_STRUCT(response);
517         ZERO_STRUCT(request);
518         
519         strncpy(request.data.winsreq, argp->key.name, sizeof(request.data.winsreq) - 1);
520         request.data.winsreq[sizeof(request.data.winsreq) - 1] = '\0';
521
522         if( (ret = winbindd_request_response(WINBINDD_WINS_BYNAME, &request, &response))
523                 == NSS_STATUS_SUCCESS ) {
524           ret = parse_response(AF_INET, argp, &response);
525         }
526
527         free_response(&response);
528         return ret;
529 }
530
531 static NSS_STATUS
532 _nss_winbind_hosts_getbyaddr(nss_backend_t* be, void *args)
533 {
534         NSS_STATUS ret;
535         struct winbindd_response response;
536         struct winbindd_request request;
537         nss_XbyY_args_t *argp = (nss_XbyY_args_t *)args;
538         const char *p;
539
540         ZERO_STRUCT(response);
541         ZERO_STRUCT(request);
542
543         /* winbindd currently does not resolve IPv6 */
544         if(argp->key.hostaddr.type == AF_INET6) {
545                 argp->h_errno = NO_DATA;
546                 return NSS_STATUS_UNAVAIL;
547         }
548
549         p = inet_ntop(argp->key.hostaddr.type, argp->key.hostaddr.addr,
550                         request.data.winsreq, INET6_ADDRSTRLEN);
551
552         ret = winbindd_request_response(WINBINDD_WINS_BYIP, &request, &response);
553
554         if( ret == NSS_STATUS_SUCCESS) {
555           parse_response(argp->key.hostaddr.type, argp, &response);
556         }
557         free_response(&response);
558         return ret;
559 }
560
561 /* winbind does not provide setent, getent, endent for wins */
562 static NSS_STATUS
563 _nss_winbind_common_endent(nss_backend_t* be, void *args)
564 {
565         return (NSS_STATUS_UNAVAIL);
566 }
567
568 static NSS_STATUS
569 _nss_winbind_common_setent(nss_backend_t* be, void *args)
570 {
571         return (NSS_STATUS_UNAVAIL);
572 }
573
574 static NSS_STATUS
575 _nss_winbind_common_getent(nss_backend_t* be, void *args)
576 {
577         return (NSS_STATUS_UNAVAIL);
578 }
579
580 static nss_backend_t*
581 _nss_winbind_common_constr (nss_backend_op_t ops[], int n_ops)
582 {
583         nss_backend_t* be;
584
585         if(!(be = SMB_MALLOC_P(nss_backend_t)) )
586         return NULL;
587
588         be->ops = ops;
589         be->n_ops = n_ops;
590
591         return be;
592 }
593
594 static NSS_STATUS
595 _nss_winbind_common_destr (nss_backend_t* be, void* args)
596 {
597         SAFE_FREE(be);
598         return NSS_STATUS_SUCCESS;
599 }
600
601 static nss_backend_op_t ipnodes_ops[] = {
602         _nss_winbind_common_destr,
603         _nss_winbind_common_endent,
604         _nss_winbind_common_setent,
605         _nss_winbind_common_getent,
606         _nss_winbind_ipnodes_getbyname,
607         _nss_winbind_hosts_getbyaddr,
608 };
609
610 nss_backend_t *
611 _nss_winbind_ipnodes_constr(dummy1, dummy2, dummy3)
612         const char      *dummy1, *dummy2, *dummy3;
613 {
614         return (_nss_winbind_common_constr(ipnodes_ops,
615                 sizeof (ipnodes_ops) / sizeof (ipnodes_ops[0])));
616 }
617
618 static nss_backend_op_t host_ops[] = {
619         _nss_winbind_common_destr,
620         _nss_winbind_common_endent,
621         _nss_winbind_common_setent,
622         _nss_winbind_common_getent,
623         _nss_winbind_hosts_getbyname,
624         _nss_winbind_hosts_getbyaddr,
625 };
626
627 nss_backend_t *
628 _nss_winbind_hosts_constr(dummy1, dummy2, dummy3)
629         const char      *dummy1, *dummy2, *dummy3;
630 {
631         return (_nss_winbind_common_constr(host_ops,
632                 sizeof (host_ops) / sizeof (host_ops[0])));
633 }
634
635 #endif  /* defined(SUNOS5) */
636 #endif  /* defined(HAVE_NSS_COMMON_H) || defined(HPUX) */