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