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