943a1973ef32f49d21a3ef6598653bcee4fc01ca
[samba.git] / nsswitch / winbind_nss_aix.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    AIX loadable authentication module, providing identification and
5    authentication routines against Samba winbind/Windows NT Domain
6
7    Copyright (C) Tim Potter 2003
8    Copyright (C) Steve Roylance 2003
9    Copyright (C) Andrew Tridgell 2003-2004
10
11    This library is free software; you can redistribute it and/or
12    modify it under the terms of the GNU Lesser General Public
13    License as published by the Free Software Foundation; either
14    version 3 of the License, or (at your option) any later version.
15
16    This library is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19    Library General Public License for more details.
20
21    You should have received a copy of the GNU Lesser General Public License
22    along with this program.  If not, see <http://www.gnu.org/licenses/>.
23 */
24
25 /*
26
27   To install this module copy nsswitch/WINBIND to /usr/lib/security and add
28   "WINBIND" in /usr/lib/security/methods.cfg and /etc/security/user
29
30   Note that this module also provides authentication and password
31   changing routines, so you do not need to install the winbind PAM
32   module.
33
34   see
35   http://publib16.boulder.ibm.com/doc_link/en_US/a_doc_lib/aixprggd/kernextc/sec_load_mod.htm
36   for some information in the interface that this module implements
37
38   Many thanks to Julianne Haugh for explaining some of the finer
39   details of this interface.
40
41   To debug this module use uess_test.c (which you can get from tridge)
42   or set "options=debug" in /usr/lib/security/methods.cfg
43
44 */
45
46 #include "winbind_client.h"
47 #include <usersec.h>
48
49 /* enable this to log which entry points have not been
50   completed yet */
51 #define LOG_UNIMPLEMENTED_CALLS 0
52
53
54 #define WB_AIX_ENCODED '_'
55
56 static int debug_enabled;
57
58
59 static void logit(const char *format, ...)
60 {
61         va_list ap;
62         FILE *f;
63         if (!debug_enabled) {
64                 return;
65         }
66         f = fopen("/tmp/WINBIND_DEBUG.log", "a");
67         if (!f) return;
68         va_start(ap, format);
69         vfprintf(f, format, ap);
70         va_end(ap);
71         fclose(f);
72 }
73
74
75 #define HANDLE_ERRORS(ret) do { \
76         if ((ret) == NSS_STATUS_NOTFOUND) { \
77                 errno = ENOENT; \
78                 return NULL; \
79         } else if ((ret) != NSS_STATUS_SUCCESS) { \
80                 errno = EIO; \
81                 return NULL; \
82         } \
83 } while (0)
84
85 #define STRCPY_RET(dest, src) \
86 do { \
87         if (strlen(src)+1 > sizeof(dest)) { errno = EINVAL; return -1; } \
88         strncpy(dest, src, sizeof(dest)); \
89         dest[sizeof(dest)-1] = '\0'; \
90 } while (0)
91
92 #define STRCPY_RETNULL(dest, src) \
93 do { \
94         if (strlen(src)+1 > sizeof(dest)) { errno = EINVAL; return NULL; } \
95         strncpy(dest, src, sizeof(dest)); \
96         dest[sizeof(dest)-1] = '\0'; \
97 } while (0)
98
99
100 /* free a passwd structure */
101 static void free_pwd(struct passwd *pwd)
102 {
103         free(pwd->pw_name);
104         free(pwd->pw_passwd);
105         free(pwd->pw_gecos);
106         free(pwd->pw_dir);
107         free(pwd->pw_shell);
108         free(pwd);
109 }
110
111 /* free a group structure */
112 static void free_grp(struct group *grp)
113 {
114         int i;
115
116         free(grp->gr_name);
117         free(grp->gr_passwd);
118
119         if (!grp->gr_mem) {
120                 free(grp);
121                 return;
122         }
123
124         for (i=0; grp->gr_mem[i]; i++) {
125                 free(grp->gr_mem[i]);
126         }
127
128         free(grp->gr_mem);
129         free(grp);
130 }
131
132
133 /* replace commas with nulls, and null terminate */
134 static void replace_commas(char *s)
135 {
136         char *p, *p0=s;
137         for (p=strchr(s, ','); p; p = strchr(p+1, ',')) {
138                 *p=0;
139                 p0 = p+1;
140         }
141
142         p0[strlen(p0)+1] = 0;
143 }
144
145
146 /* the decode_*() routines are used to cope with the fact that AIX 5.2
147    and below cannot handle user or group names longer than 8
148    characters in some interfaces. We use the normalize method to
149    provide a mapping to a username that fits, by using the form '_UID'
150    or '_GID'.
151
152    this only works if you can guarantee that the WB_AIX_ENCODED char
153    is not used as the first char of any other username
154 */
155 static unsigned decode_id(const char *name)
156 {
157         unsigned id;
158         sscanf(name+1, "%u", &id);
159         return id;
160 }
161
162 static struct passwd *wb_aix_getpwuid(uid_t uid);
163
164 static char *decode_user(const char *name)
165 {
166         struct passwd *pwd;
167         unsigned id;
168         char *ret;
169
170         sscanf(name+1, "%u", &id);
171         pwd = wb_aix_getpwuid(id);
172         if (!pwd) {
173                 return NULL;
174         }
175         ret = strdup(pwd->pw_name);
176
177         free_pwd(pwd);
178
179         logit("decoded '%s' -> '%s'\n", name, ret);
180
181         return ret;
182 }
183
184
185 /*
186   fill a struct passwd from a winbindd_pw struct, allocating as a single block
187 */
188 static struct passwd *fill_pwent(struct winbindd_pw *pw)
189 {
190         struct passwd *result;
191
192         result = calloc(1, sizeof(struct passwd));
193         if (!result) {
194                 errno = ENOMEM;
195                 return NULL;
196         }
197
198         result->pw_uid = pw->pw_uid;
199         result->pw_gid = pw->pw_gid;
200         result->pw_name   = strdup(pw->pw_name);
201         result->pw_passwd = strdup(pw->pw_passwd);
202         result->pw_gecos  = strdup(pw->pw_gecos);
203         result->pw_dir    = strdup(pw->pw_dir);
204         result->pw_shell  = strdup(pw->pw_shell);
205
206         return result;
207 }
208
209
210 /*
211   fill a struct group from a winbindd_pw struct, allocating as a single block
212 */
213 static struct group *fill_grent(struct winbindd_gr *gr, char *gr_mem)
214 {
215         int i;
216         struct group *result;
217         char *p, *name;
218
219         result = calloc(1, sizeof(struct group));
220         if (!result) {
221                 errno = ENOMEM;
222                 return NULL;
223         }
224
225         result->gr_gid = gr->gr_gid;
226
227         result->gr_name   = strdup(gr->gr_name);
228         result->gr_passwd = strdup(gr->gr_passwd);
229
230         /* Group membership */
231         if ((gr->num_gr_mem < 0) || !gr_mem) {
232                 gr->num_gr_mem = 0;
233         }
234
235         if (gr->num_gr_mem == 0) {
236                 /* Group is empty */
237                 return result;
238         }
239
240         result->gr_mem = (char **)malloc(sizeof(char *) * (gr->num_gr_mem+1));
241         if (!result->gr_mem) {
242                 free(result->gr_name);
243                 free(result->gr_passwd);
244                 free(result);
245                 errno = ENOMEM;
246                 return NULL;
247         }
248
249         /* Start looking at extra data */
250         i=0;
251         for (name = strtok_r(gr_mem, ",", &p);
252              name;
253              name = strtok_r(NULL, ",", &p)) {
254                 if (i == gr->num_gr_mem) {
255                         break;
256                 }
257                 result->gr_mem[i] = strdup(name);
258                 i++;
259         }
260
261         /* Terminate list */
262         result->gr_mem[i] = NULL;
263
264         return result;
265 }
266
267
268
269 /* take a group id and return a filled struct group */
270 static struct group *wb_aix_getgrgid(gid_t gid)
271 {
272         struct winbindd_response response;
273         struct winbindd_request request;
274         struct group *grp;
275         NSS_STATUS ret;
276
277         logit("getgrgid %d\n", gid);
278
279         ZERO_STRUCT(response);
280         ZERO_STRUCT(request);
281
282         request.data.gid = gid;
283
284         ret = winbindd_request_response(NULL, WINBINDD_GETGRGID,
285                                         &request, &response);
286
287         logit("getgrgid ret=%d\n", ret);
288
289         HANDLE_ERRORS(ret);
290
291         grp = fill_grent(&response.data.gr, response.extra_data.data);
292
293         winbindd_free_response(&response);
294
295         return grp;
296 }
297
298 /* take a group name and return a filled struct group */
299 static struct group *wb_aix_getgrnam(const char *name)
300 {
301         struct winbindd_response response;
302         struct winbindd_request request;
303         NSS_STATUS ret;
304         struct group *grp;
305
306         if (*name == WB_AIX_ENCODED) {
307                 return wb_aix_getgrgid(decode_id(name));
308         }
309
310         logit("getgrnam '%s'\n", name);
311
312         ZERO_STRUCT(response);
313         ZERO_STRUCT(request);
314
315         STRCPY_RETNULL(request.data.groupname, name);
316
317         ret = winbindd_request_response(NULL, WINBINDD_GETGRNAM,
318                                         &request, &response);
319
320         HANDLE_ERRORS(ret);
321
322         grp = fill_grent(&response.data.gr, response.extra_data.data);
323
324         winbindd_free_response(&response);
325
326         return grp;
327 }
328
329
330 /* this call doesn't have to fill in the gr_mem, but we do anyway
331    for simplicity */
332 static struct group *wb_aix_getgracct(void *id, int type)
333 {
334         if (type == 1) {
335                 return wb_aix_getgrnam((char *)id);
336         }
337         if (type == 0) {
338                 return wb_aix_getgrgid(*(int *)id);
339         }
340         errno = EINVAL;
341         return NULL;
342 }
343
344
345 /* take a username and return a string containing a comma-separated
346    list of group id numbers to which the user belongs */
347 static char *wb_aix_getgrset(char *user)
348 {
349         struct winbindd_response response;
350         struct winbindd_request request;
351         NSS_STATUS ret;
352         int i, idx;
353         char *tmpbuf;
354         int num_gids;
355         gid_t *gid_list;
356         char *r_user = user;
357
358         if (*user == WB_AIX_ENCODED) {
359                 r_user = decode_user(r_user);
360                 if (!r_user) {
361                         errno = ENOENT;
362                         return NULL;
363                 }
364         }
365
366         logit("getgrset '%s'\n", r_user);
367
368         ZERO_STRUCT(response);
369         ZERO_STRUCT(request);
370
371         STRCPY_RETNULL(request.data.username, r_user);
372
373         if (*user == WB_AIX_ENCODED) {
374                 free(r_user);
375         }
376
377         ret = winbindd_request_response(NULL, WINBINDD_GETGROUPS,
378                                         &request, &response);
379
380         HANDLE_ERRORS(ret);
381
382         num_gids = response.data.num_entries;
383         gid_list = (gid_t *)response.extra_data.data;
384
385         /* allocate a space large enough to contruct the string */
386         tmpbuf = malloc(num_gids*12);
387         if (!tmpbuf) {
388                 return NULL;
389         }
390
391         for (idx=i=0; i < num_gids-1; i++) {
392                 idx += sprintf(tmpbuf+idx, "%u,", gid_list[i]);
393         }
394         idx += sprintf(tmpbuf+idx, "%u", gid_list[i]);
395
396         winbindd_free_response(&response);
397
398         return tmpbuf;
399 }
400
401
402 /* take a uid and return a filled struct passwd */
403 static struct passwd *wb_aix_getpwuid(uid_t uid)
404 {
405         struct winbindd_response response;
406         struct winbindd_request request;
407         NSS_STATUS ret;
408         struct passwd *pwd;
409
410         logit("getpwuid '%d'\n", uid);
411
412         ZERO_STRUCT(response);
413         ZERO_STRUCT(request);
414
415         request.data.uid = uid;
416
417         ret = winbindd_request_response(NULL, WINBINDD_GETPWUID,
418                                         &request, &response);
419
420         HANDLE_ERRORS(ret);
421
422         pwd = fill_pwent(&response.data.pw);
423
424         winbindd_free_response(&response);
425
426         logit("getpwuid gave ptr %p\n", pwd);
427
428         return pwd;
429 }
430
431
432 /* take a username and return a filled struct passwd */
433 static struct passwd *wb_aix_getpwnam(const char *name)
434 {
435         struct winbindd_response response;
436         struct winbindd_request request;
437         NSS_STATUS ret;
438         struct passwd *pwd;
439
440         if (*name == WB_AIX_ENCODED) {
441                 return wb_aix_getpwuid(decode_id(name));
442         }
443
444         logit("getpwnam '%s'\n", name);
445
446         ZERO_STRUCT(response);
447         ZERO_STRUCT(request);
448
449         STRCPY_RETNULL(request.data.username, name);
450
451         ret = winbindd_request_response(NULL, WINBINDD_GETPWNAM,
452                                         &request, &response);
453
454         HANDLE_ERRORS(ret);
455
456         pwd = fill_pwent(&response.data.pw);
457
458         winbindd_free_response(&response);
459
460         logit("getpwnam gave ptr %p\n", pwd);
461
462         return pwd;
463 }
464
465 /*
466   list users
467 */
468 static int wb_aix_lsuser(char *attributes[], attrval_t results[], int size)
469 {
470         NSS_STATUS ret;
471         struct winbindd_request request;
472         struct winbindd_response response;
473         int len;
474         char *s;
475
476         if (size != 1 || strcmp(attributes[0], S_USERS) != 0) {
477                 logit("invalid lsuser op\n");
478                 errno = EINVAL;
479                 return -1;
480         }
481
482         ZERO_STRUCT(request);
483         ZERO_STRUCT(response);
484
485         ret = winbindd_request_response(NULL, WINBINDD_LIST_USERS,
486                                         &request, &response);
487         if (ret != 0) {
488                 errno = EINVAL;
489                 return -1;
490         }
491
492         len = strlen(response.extra_data.data);
493
494         s = malloc(len+2);
495         if (!s) {
496                 winbindd_free_response(&response);
497                 errno = ENOMEM;
498                 return -1;
499         }
500
501         memcpy(s, response.extra_data.data, len+1);
502
503         replace_commas(s);
504
505         results[0].attr_un.au_char = s;
506         results[0].attr_flag = 0;
507
508         winbindd_free_response(&response);
509
510         return 0;
511 }
512
513
514 /*
515   list groups
516 */
517 static int wb_aix_lsgroup(char *attributes[], attrval_t results[], int size)
518 {
519         NSS_STATUS ret;
520         struct winbindd_request request;
521         struct winbindd_response response;
522         int len;
523         char *s;
524
525         if (size != 1 || strcmp(attributes[0], S_GROUPS) != 0) {
526                 logit("invalid lsgroup op\n");
527                 errno = EINVAL;
528                 return -1;
529         }
530
531         ZERO_STRUCT(request);
532         ZERO_STRUCT(response);
533
534         ret = winbindd_request_response(NULL, WINBINDD_LIST_GROUPS,
535                                         &request, &response);
536         if (ret != 0) {
537                 errno = EINVAL;
538                 return -1;
539         }
540
541         len = strlen(response.extra_data.data);
542
543         s = malloc(len+2);
544         if (!s) {
545                 winbindd_free_response(&response);
546                 errno = ENOMEM;
547                 return -1;
548         }
549
550         memcpy(s, response.extra_data.data, len+1);
551
552         replace_commas(s);
553
554         results[0].attr_un.au_char = s;
555         results[0].attr_flag = 0;
556
557         winbindd_free_response(&response);
558
559         return 0;
560 }
561
562
563 static attrval_t pwd_to_group(struct passwd *pwd)
564 {
565         attrval_t r;
566         struct group *grp = wb_aix_getgrgid(pwd->pw_gid);
567
568         if (!grp) {
569                 r.attr_flag = EINVAL;
570         } else {
571                 r.attr_flag = 0;
572                 r.attr_un.au_char = strdup(grp->gr_name);
573                 free_grp(grp);
574         }
575
576         return r;
577 }
578
579 static attrval_t pwd_to_groupsids(struct passwd *pwd)
580 {
581         attrval_t r;
582         char *s, *p;
583         size_t mlen;
584
585         if ( (s = wb_aix_getgrset(pwd->pw_name)) == NULL ) {
586                 r.attr_flag = EINVAL;
587                 return r;
588         }
589
590         mlen = strlen(s)+2;
591         if ( (p = malloc(mlen)) == NULL ) {
592                 r.attr_flag = ENOMEM;
593                 return r;
594         }
595
596         strncpy(p, s, mlen);
597         p[mlen-1] = '\0';
598         replace_commas(p);
599         free(s);
600
601         r.attr_un.au_char = p;
602
603         return r;
604 }
605
606 static attrval_t pwd_to_sid(struct passwd *pwd)
607 {
608         struct winbindd_request request;
609         struct winbindd_response response;
610         attrval_t r;
611
612         ZERO_STRUCT(request);
613         ZERO_STRUCT(response);
614
615         request.data.uid = pwd->pw_uid;
616
617 #if 0
618         /*
619          * Removed because WINBINDD_UID_TO_SID is replaced by
620          * WINBINDD_XIDS_TO_SIDS. I don't have an AIX build
621          * environment around, so I did not convert this call. If
622          * someone stumbles over this, please contact me:
623          * vl@samba.org, I'll convert this.
624          */
625         if (winbindd_request_response(NULL, WINBINDD_UID_TO_SID,
626                                       &request, &response) !=
627             NSS_STATUS_SUCCESS) {
628                 r.attr_flag = ENOENT;
629         } else {
630                 r.attr_flag = 0;
631                 r.attr_un.au_char = strdup(response.data.sid.sid);
632         }
633 #else
634         r.attr_flag = ENOENT;
635 #endif
636
637         return r;
638 }
639
640 static int wb_aix_user_attrib(const char *key, char *attributes[],
641                               attrval_t results[], int size)
642 {
643         struct passwd *pwd;
644         int i;
645
646         pwd = wb_aix_getpwnam(key);
647         if (!pwd) {
648                 errno = ENOENT;
649                 return -1;
650         }
651
652         for (i=0;i<size;i++) {
653                 results[i].attr_flag = 0;
654
655                 if (strcmp(attributes[i], S_ID) == 0) {
656                         results[i].attr_un.au_int = pwd->pw_uid;
657 #ifdef _AIXVERSION_530
658                 } else if (strcmp(attributes[i], S_PGID) == 0) {
659                         results[i].attr_un.au_int = pwd->pw_gid;
660 #endif
661                 } else if (strcmp(attributes[i], S_PWD) == 0) {
662                         results[i].attr_un.au_char = strdup(pwd->pw_passwd);
663                 } else if (strcmp(attributes[i], S_HOME) == 0) {
664                         results[i].attr_un.au_char = strdup(pwd->pw_dir);
665                 } else if (strcmp(attributes[i], S_SHELL) == 0) {
666                         results[i].attr_un.au_char = strdup(pwd->pw_shell);
667                 } else if (strcmp(attributes[i], S_REGISTRY) == 0) {
668                         results[i].attr_un.au_char = strdup("WINBIND");
669                 } else if (strcmp(attributes[i], S_GECOS) == 0) {
670                         results[i].attr_un.au_char = strdup(pwd->pw_gecos);
671                 } else if (strcmp(attributes[i], S_PGRP) == 0) {
672                         results[i] = pwd_to_group(pwd);
673                 } else if (strcmp(attributes[i], S_GROUPS) == 0) {
674                         results[i] = pwd_to_groupsids(pwd);
675                 } else if (strcmp(attributes[i], S_GROUPSIDS) == 0) {
676                         results[i] = pwd_to_groupsids(pwd);
677                 } else if (strcmp(attributes[i], "SID") == 0) {
678                         results[i] = pwd_to_sid(pwd);
679                 } else {
680                         logit("Unknown user attribute '%s'\n", attributes[i]);
681                         results[i].attr_flag = EINVAL;
682                 }
683         }
684
685         free_pwd(pwd);
686
687         return 0;
688 }
689
690 static int wb_aix_group_attrib(const char *key, char *attributes[],
691                                attrval_t results[], int size)
692 {
693         struct group *grp;
694         int i;
695
696         grp = wb_aix_getgrnam(key);
697         if (!grp) {
698                 errno = ENOENT;
699                 return -1;
700         }
701
702         for (i=0;i<size;i++) {
703                 results[i].attr_flag = 0;
704
705                 if (strcmp(attributes[i], S_PWD) == 0) {
706                         results[i].attr_un.au_char = strdup(grp->gr_passwd);
707                 } else if (strcmp(attributes[i], S_ID) == 0) {
708                         results[i].attr_un.au_int = grp->gr_gid;
709                 } else {
710                         logit("Unknown group attribute '%s'\n", attributes[i]);
711                         results[i].attr_flag = EINVAL;
712                 }
713         }
714
715         free_grp(grp);
716
717         return 0;
718 }
719
720
721 /*
722   called for user/group enumerations
723 */
724 static int wb_aix_getentry(char *key, char *table, char *attributes[],
725                            attrval_t results[], int size)
726 {
727         logit("Got getentry with key='%s' table='%s' size=%d attributes[0]='%s'\n",
728               key, table, size, attributes[0]);
729
730         if (strcmp(key, "ALL") == 0 &&
731             strcmp(table, "user") == 0) {
732                 return wb_aix_lsuser(attributes, results, size);
733         }
734
735         if (strcmp(key, "ALL") == 0 &&
736             strcmp(table, "group") == 0) {
737                 return wb_aix_lsgroup(attributes, results, size);
738         }
739
740         if (strcmp(table, "user") == 0) {
741                 return wb_aix_user_attrib(key, attributes, results, size);
742         }
743
744         if (strcmp(table, "group") == 0) {
745                 return wb_aix_group_attrib(key, attributes, results, size);
746         }
747
748         logit("Unknown getentry operation key='%s' table='%s'\n", key, table);
749
750         errno = ENOSYS;
751         return -1;
752 }
753
754
755
756 /*
757   called to start the backend
758 */
759 static void *wb_aix_open(const char *name, const char *domain, int mode, char *options)
760 {
761         if (strstr(options, "debug")) {
762                 debug_enabled = 1;
763         }
764         logit("open name='%s' mode=%d domain='%s' options='%s'\n", name, domain,
765               mode, options);
766         return NULL;
767 }
768
769 static void wb_aix_close(void *token)
770 {
771         logit("close\n");
772         return;
773 }
774
775 #ifdef HAVE_STRUCT_SECMETHOD_TABLE_METHOD_ATTRLIST
776 /*
777    return a list of additional attributes supported by the backend
778 */
779 static attrlist_t **wb_aix_attrlist(void)
780 {
781         /* pretty confusing but we are allocating the array of pointers
782            and the structures we'll be pointing to all at once.  So
783            you need N+1 pointers and N structures. */
784
785         attrlist_t **ret = NULL;
786         attrlist_t *offset = NULL;
787         int i;
788         int n;
789         size_t size;
790
791         struct attr_types {
792                 const char *name;
793                 int flags;
794                 int type;
795         } attr_list[] = {
796                 /* user attributes */
797                 {S_ID,          AL_USERATTR,    SEC_INT},
798                 {S_PGRP,        AL_USERATTR,    SEC_CHAR},
799                 {S_HOME,        AL_USERATTR,    SEC_CHAR},
800                 {S_SHELL,       AL_USERATTR,    SEC_CHAR},
801 #ifdef _AIXVERSION_530
802                 {S_PGID,        AL_USERATTR,    SEC_INT},
803 #endif
804                 {S_GECOS,       AL_USERATTR,    SEC_CHAR},
805                 {S_SHELL,       AL_USERATTR,    SEC_CHAR},
806                 {S_PGRP,        AL_USERATTR,    SEC_CHAR},
807                 {S_GROUPS,      AL_USERATTR,    SEC_LIST},
808                 {S_GROUPSIDS,   AL_USERATTR,    SEC_LIST},
809                 {"SID",         AL_USERATTR,    SEC_CHAR},
810
811                 /* group attributes */
812                 {S_ID,          AL_GROUPATTR,   SEC_INT}
813         };
814
815         logit("method attrlist called\n");
816
817         n = sizeof(attr_list) / sizeof(struct attr_types);
818         size = (n*sizeof(attrlist_t *));
819
820         if ( (ret = malloc( size )) == NULL ) {
821                 errno = ENOMEM;
822                 return NULL;
823         }
824
825         /* offset to where the structures start in the buffer */
826
827         offset = (attrlist_t *)(ret + n);
828
829         /* now loop over the user_attr_list[] array and add
830            all the members */
831
832         for ( i=0; i<n; i++ ) {
833                 attrlist_t *a = malloc(sizeof(attrlist_t));
834
835                 if ( !a ) {
836                         /* this is bad.  Just bail */
837                         return NULL;
838                 }
839
840                 a->al_name  = strdup(attr_list[i].name);
841                 a->al_flags = attr_list[i].flags;
842                 a->al_type  = attr_list[i].type;
843
844                 ret[i] = a;
845         }
846         ret[n] = NULL;
847
848         return ret;
849 }
850 #endif
851
852
853 /*
854   turn a long username into a short one. Needed to cope with the 8 char
855   username limit in AIX 5.2 and below
856 */
857 static int wb_aix_normalize(char *longname, char *shortname)
858 {
859         struct passwd *pwd;
860
861         logit("normalize '%s'\n", longname);
862
863         /* automatically cope with AIX 5.3 with longer usernames
864            when it comes out */
865         if (S_NAMELEN > strlen(longname)) {
866                 strncpy(shortname, longname, S_NAMELEN);
867                 shortname[S_NAMELEN-1] = '\0';
868                 return 1;
869         }
870
871         pwd = wb_aix_getpwnam(longname);
872         if (!pwd) {
873                 errno = ENOENT;
874                 return 0;
875         }
876
877         sprintf(shortname, "%c%07u", WB_AIX_ENCODED, pwd->pw_uid);
878
879         free_pwd(pwd);
880
881         return 1;
882 }
883
884
885 /*
886   authenticate a user
887  */
888 static int wb_aix_authenticate(char *user, char *pass,
889                                int *reenter, char **message)
890 {
891         struct winbindd_request request;
892         struct winbindd_response response;
893         NSS_STATUS result;
894         char *r_user = user;
895
896         logit("authenticate '%s' response='%s'\n", user, pass);
897
898         *reenter = 0;
899         *message = NULL;
900
901         /* Send off request */
902         ZERO_STRUCT(request);
903         ZERO_STRUCT(response);
904
905         if (*user == WB_AIX_ENCODED) {
906                 r_user = decode_user(r_user);
907                 if (!r_user) {
908                         return AUTH_NOTFOUND;
909                 }
910         }
911
912         STRCPY_RET(request.data.auth.user, r_user);
913         STRCPY_RET(request.data.auth.pass, pass);
914
915         if (*user == WB_AIX_ENCODED) {
916                 free(r_user);
917         }
918
919         result = winbindd_request_response(NULL, WINBINDD_PAM_AUTH,
920                                            &request, &response);
921
922         winbindd_free_response(&response);
923
924         logit("auth result %d for '%s'\n", result, user);
925
926         if (result == NSS_STATUS_SUCCESS) {
927                 errno = 0;
928                 return AUTH_SUCCESS;
929         }
930
931         return AUTH_FAILURE;
932 }
933
934
935 /*
936   change a user password
937 */
938 static int wb_aix_chpass(char *user, char *oldpass, char *newpass, char **message)
939 {
940         struct winbindd_request request;
941         struct winbindd_response response;
942         NSS_STATUS result;
943         char *r_user = user;
944
945         if (*user == WB_AIX_ENCODED) {
946                 r_user = decode_user(r_user);
947                 if (!r_user) {
948                         errno = ENOENT;
949                         return -1;
950                 }
951         }
952
953         logit("chpass '%s' old='%s' new='%s'\n", r_user, oldpass, newpass);
954
955         *message = NULL;
956
957         /* Send off request */
958         ZERO_STRUCT(request);
959         ZERO_STRUCT(response);
960
961         STRCPY_RET(request.data.chauthtok.user, r_user);
962         STRCPY_RET(request.data.chauthtok.oldpass, oldpass);
963         STRCPY_RET(request.data.chauthtok.newpass, newpass);
964
965         if (*user == WB_AIX_ENCODED) {
966                 free(r_user);
967         }
968
969         result = winbindd_request_response(NULL, WINBINDD_PAM_CHAUTHTOK,
970                                            &request, &response);
971
972         winbindd_free_response(&response);
973
974         if (result == NSS_STATUS_SUCCESS) {
975                 errno = 0;
976                 return 0;
977         }
978
979         errno = EINVAL;
980         return -1;
981 }
982
983 /*
984   don't do any password strength testing for now
985 */
986 static int wb_aix_passwdrestrictions(char *user, char *newpass, char *oldpass,
987                                      char **message)
988 {
989         logit("passwdresrictions called for '%s'\n", user);
990         return 0;
991 }
992
993
994 static int wb_aix_passwdexpired(char *user, char **message)
995 {
996         logit("passwdexpired '%s'\n", user);
997         /* we should check the account bits here */
998         return 0;
999 }
1000
1001
1002 /*
1003   we can't return a crypt() password
1004 */
1005 static char *wb_aix_getpasswd(char *user)
1006 {
1007         logit("getpasswd '%s'\n", user);
1008         errno = ENOSYS;
1009         return NULL;
1010 }
1011
1012 /*
1013   this is called to update things like the last login time. We don't
1014   currently pass this onto the DC
1015 */
1016 static int wb_aix_putentry(char *key, char *table, char *attributes[],
1017                            attrval_t values[], int size)
1018 {
1019         logit("putentry key='%s' table='%s' attrib='%s'\n",
1020               key, table, size>=1?attributes[0]:"<null>");
1021         errno = ENOSYS;
1022         return -1;
1023 }
1024
1025 static int wb_aix_commit(char *key, char *table)
1026 {
1027         logit("commit key='%s' table='%s'\n");
1028         errno = ENOSYS;
1029         return -1;
1030 }
1031
1032 static int wb_aix_getgrusers(char *group, void *result, int type, int *size)
1033 {
1034         logit("getgrusers group='%s'\n", group);
1035         errno = ENOSYS;
1036         return -1;
1037 }
1038
1039
1040 #define DECL_METHOD(x) \
1041 int method_ ## x(void) \
1042 { \
1043         logit("UNIMPLEMENTED METHOD '%s'\n", #x); \
1044         errno = EINVAL; \
1045         return -1; \
1046 }
1047
1048 #if LOG_UNIMPLEMENTED_CALLS
1049 DECL_METHOD(delgroup);
1050 DECL_METHOD(deluser);
1051 DECL_METHOD(newgroup);
1052 DECL_METHOD(newuser);
1053 DECL_METHOD(putgrent);
1054 DECL_METHOD(putgrusers);
1055 DECL_METHOD(putpwent);
1056 DECL_METHOD(lock);
1057 DECL_METHOD(unlock);
1058 DECL_METHOD(getcred);
1059 DECL_METHOD(setcred);
1060 DECL_METHOD(deletecred);
1061 #endif
1062
1063 int wb_aix_init(struct secmethod_table *methods)
1064 {
1065         ZERO_STRUCTP(methods);
1066
1067 #ifdef HAVE_STRUCT_SECMETHOD_TABLE_METHOD_VERSION
1068         methods->method_version = SECMETHOD_VERSION_520;
1069 #endif
1070
1071         methods->method_getgrgid           = wb_aix_getgrgid;
1072         methods->method_getgrnam           = wb_aix_getgrnam;
1073         methods->method_getgrset           = wb_aix_getgrset;
1074         methods->method_getpwnam           = wb_aix_getpwnam;
1075         methods->method_getpwuid           = wb_aix_getpwuid;
1076         methods->method_getentry           = wb_aix_getentry;
1077         methods->method_open               = wb_aix_open;
1078         methods->method_close              = wb_aix_close;
1079         methods->method_normalize          = wb_aix_normalize;
1080         methods->method_passwdexpired      = wb_aix_passwdexpired;
1081         methods->method_putentry           = wb_aix_putentry;
1082         methods->method_getpasswd          = wb_aix_getpasswd;
1083         methods->method_authenticate       = wb_aix_authenticate;
1084         methods->method_commit             = wb_aix_commit;
1085         methods->method_chpass             = wb_aix_chpass;
1086         methods->method_passwdrestrictions = wb_aix_passwdrestrictions;
1087         methods->method_getgracct          = wb_aix_getgracct;
1088         methods->method_getgrusers         = wb_aix_getgrusers;
1089 #ifdef HAVE_STRUCT_SECMETHOD_TABLE_METHOD_ATTRLIST
1090         methods->method_attrlist           = wb_aix_attrlist;
1091 #endif
1092
1093 #if LOG_UNIMPLEMENTED_CALLS
1094         methods->method_delgroup      = method_delgroup;
1095         methods->method_deluser       = method_deluser;
1096         methods->method_newgroup      = method_newgroup;
1097         methods->method_newuser       = method_newuser;
1098         methods->method_putgrent      = method_putgrent;
1099         methods->method_putgrusers    = method_putgrusers;
1100         methods->method_putpwent      = method_putpwent;
1101         methods->method_lock          = method_lock;
1102         methods->method_unlock        = method_unlock;
1103         methods->method_getcred       = method_getcred;
1104         methods->method_setcred       = method_setcred;
1105         methods->method_deletecred    = method_deletecred;
1106 #endif
1107
1108         return AUTH_SUCCESS;
1109 }