dsdb: Create rootdse_get_private_data()
[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], "SID") == 0) {
676                         results[i] = pwd_to_sid(pwd);
677                 } else {
678                         logit("Unknown user attribute '%s'\n", attributes[i]);
679                         results[i].attr_flag = EINVAL;
680                 }
681         }
682
683         free_pwd(pwd);
684
685         return 0;
686 }
687
688 static int wb_aix_group_attrib(const char *key, char *attributes[],
689                                attrval_t results[], int size)
690 {
691         struct group *grp;
692         int i;
693
694         grp = wb_aix_getgrnam(key);
695         if (!grp) {
696                 errno = ENOENT;
697                 return -1;
698         }
699
700         for (i=0;i<size;i++) {
701                 results[i].attr_flag = 0;
702
703                 if (strcmp(attributes[i], S_PWD) == 0) {
704                         results[i].attr_un.au_char = strdup(grp->gr_passwd);
705                 } else if (strcmp(attributes[i], S_ID) == 0) {
706                         results[i].attr_un.au_int = grp->gr_gid;
707                 } else {
708                         logit("Unknown group attribute '%s'\n", attributes[i]);
709                         results[i].attr_flag = EINVAL;
710                 }
711         }
712
713         free_grp(grp);
714
715         return 0;
716 }
717
718
719 /*
720   called for user/group enumerations
721 */
722 static int wb_aix_getentry(char *key, char *table, char *attributes[],
723                            attrval_t results[], int size)
724 {
725         logit("Got getentry with key='%s' table='%s' size=%d attributes[0]='%s'\n",
726               key, table, size, attributes[0]);
727
728         if (strcmp(key, "ALL") == 0 &&
729             strcmp(table, "user") == 0) {
730                 return wb_aix_lsuser(attributes, results, size);
731         }
732
733         if (strcmp(key, "ALL") == 0 &&
734             strcmp(table, "group") == 0) {
735                 return wb_aix_lsgroup(attributes, results, size);
736         }
737
738         if (strcmp(table, "user") == 0) {
739                 return wb_aix_user_attrib(key, attributes, results, size);
740         }
741
742         if (strcmp(table, "group") == 0) {
743                 return wb_aix_group_attrib(key, attributes, results, size);
744         }
745
746         logit("Unknown getentry operation key='%s' table='%s'\n", key, table);
747
748         errno = ENOSYS;
749         return -1;
750 }
751
752
753
754 /*
755   called to start the backend
756 */
757 static void *wb_aix_open(const char *name, const char *domain, int mode, char *options)
758 {
759         if (strstr(options, "debug")) {
760                 debug_enabled = 1;
761         }
762         logit("open name='%s' mode=%d domain='%s' options='%s'\n", name, domain,
763               mode, options);
764         return NULL;
765 }
766
767 static void wb_aix_close(void *token)
768 {
769         logit("close\n");
770         return;
771 }
772
773 #ifdef HAVE_STRUCT_SECMETHOD_TABLE_METHOD_ATTRLIST
774 /*
775    return a list of additional attributes supported by the backend
776 */
777 static attrlist_t **wb_aix_attrlist(void)
778 {
779         /* pretty confusing but we are allocating the array of pointers
780            and the structures we'll be pointing to all at once.  So
781            you need N+1 pointers and N structures. */
782
783         attrlist_t **ret = NULL;
784         attrlist_t *offset = NULL;
785         int i;
786         int n;
787         size_t size;
788
789         struct attr_types {
790                 const char *name;
791                 int flags;
792                 int type;
793         } attr_list[] = {
794                 /* user attributes */
795                 {S_ID,          AL_USERATTR,    SEC_INT},
796                 {S_PGRP,        AL_USERATTR,    SEC_CHAR},
797                 {S_HOME,        AL_USERATTR,    SEC_CHAR},
798                 {S_SHELL,       AL_USERATTR,    SEC_CHAR},
799 #ifdef _AIXVERSION_530
800                 {S_PGID,        AL_USERATTR,    SEC_INT},
801 #endif
802                 {S_GECOS,       AL_USERATTR,    SEC_CHAR},
803                 {S_SHELL,       AL_USERATTR,    SEC_CHAR},
804                 {S_PGRP,        AL_USERATTR,    SEC_CHAR},
805                 {S_GROUPS,      AL_USERATTR,    SEC_LIST},
806                 {"SID",         AL_USERATTR,    SEC_CHAR},
807
808                 /* group attributes */
809                 {S_ID,          AL_GROUPATTR,   SEC_INT}
810         };
811
812         logit("method attrlist called\n");
813
814         n = sizeof(attr_list) / sizeof(struct attr_types);
815         size = (n*sizeof(attrlist_t *));
816
817         if ( (ret = malloc( size )) == NULL ) {
818                 errno = ENOMEM;
819                 return NULL;
820         }
821
822         /* offset to where the structures start in the buffer */
823
824         offset = (attrlist_t *)(ret + n);
825
826         /* now loop over the user_attr_list[] array and add
827            all the members */
828
829         for ( i=0; i<n; i++ ) {
830                 attrlist_t *a = malloc(sizeof(attrlist_t));
831
832                 if ( !a ) {
833                         /* this is bad.  Just bail */
834                         return NULL;
835                 }
836
837                 a->al_name  = strdup(attr_list[i].name);
838                 a->al_flags = attr_list[i].flags;
839                 a->al_type  = attr_list[i].type;
840
841                 ret[i] = a;
842         }
843         ret[n] = NULL;
844
845         return ret;
846 }
847 #endif
848
849
850 /*
851   turn a long username into a short one. Needed to cope with the 8 char
852   username limit in AIX 5.2 and below
853 */
854 static int wb_aix_normalize(char *longname, char *shortname)
855 {
856         struct passwd *pwd;
857
858         logit("normalize '%s'\n", longname);
859
860         /* automatically cope with AIX 5.3 with longer usernames
861            when it comes out */
862         if (S_NAMELEN > strlen(longname)) {
863                 strncpy(shortname, longname, S_NAMELEN);
864                 shortname[S_NAMELEN-1] = '\0';
865                 return 1;
866         }
867
868         pwd = wb_aix_getpwnam(longname);
869         if (!pwd) {
870                 errno = ENOENT;
871                 return 0;
872         }
873
874         sprintf(shortname, "%c%07u", WB_AIX_ENCODED, pwd->pw_uid);
875
876         free_pwd(pwd);
877
878         return 1;
879 }
880
881
882 /*
883   authenticate a user
884  */
885 static int wb_aix_authenticate(char *user, char *pass,
886                                int *reenter, char **message)
887 {
888         struct winbindd_request request;
889         struct winbindd_response response;
890         NSS_STATUS result;
891         char *r_user = user;
892
893         logit("authenticate '%s' response='%s'\n", user, pass);
894
895         *reenter = 0;
896         *message = NULL;
897
898         /* Send off request */
899         ZERO_STRUCT(request);
900         ZERO_STRUCT(response);
901
902         if (*user == WB_AIX_ENCODED) {
903                 r_user = decode_user(r_user);
904                 if (!r_user) {
905                         return AUTH_NOTFOUND;
906                 }
907         }
908
909         STRCPY_RET(request.data.auth.user, r_user);
910         STRCPY_RET(request.data.auth.pass, pass);
911
912         if (*user == WB_AIX_ENCODED) {
913                 free(r_user);
914         }
915
916         result = winbindd_request_response(NULL, WINBINDD_PAM_AUTH,
917                                            &request, &response);
918
919         winbindd_free_response(&response);
920
921         logit("auth result %d for '%s'\n", result, user);
922
923         if (result == NSS_STATUS_SUCCESS) {
924                 errno = 0;
925                 return AUTH_SUCCESS;
926         }
927
928         return AUTH_FAILURE;
929 }
930
931
932 /*
933   change a user password
934 */
935 static int wb_aix_chpass(char *user, char *oldpass, char *newpass, char **message)
936 {
937         struct winbindd_request request;
938         struct winbindd_response response;
939         NSS_STATUS result;
940         char *r_user = user;
941
942         if (*user == WB_AIX_ENCODED) {
943                 r_user = decode_user(r_user);
944                 if (!r_user) {
945                         errno = ENOENT;
946                         return -1;
947                 }
948         }
949
950         logit("chpass '%s' old='%s' new='%s'\n", r_user, oldpass, newpass);
951
952         *message = NULL;
953
954         /* Send off request */
955         ZERO_STRUCT(request);
956         ZERO_STRUCT(response);
957
958         STRCPY_RET(request.data.chauthtok.user, r_user);
959         STRCPY_RET(request.data.chauthtok.oldpass, oldpass);
960         STRCPY_RET(request.data.chauthtok.newpass, newpass);
961
962         if (*user == WB_AIX_ENCODED) {
963                 free(r_user);
964         }
965
966         result = winbindd_request_response(NULL, WINBINDD_PAM_CHAUTHTOK,
967                                            &request, &response);
968
969         winbindd_free_response(&response);
970
971         if (result == NSS_STATUS_SUCCESS) {
972                 errno = 0;
973                 return 0;
974         }
975
976         errno = EINVAL;
977         return -1;
978 }
979
980 /*
981   don't do any password strength testing for now
982 */
983 static int wb_aix_passwdrestrictions(char *user, char *newpass, char *oldpass,
984                                      char **message)
985 {
986         logit("passwdresrictions called for '%s'\n", user);
987         return 0;
988 }
989
990
991 static int wb_aix_passwdexpired(char *user, char **message)
992 {
993         logit("passwdexpired '%s'\n", user);
994         /* we should check the account bits here */
995         return 0;
996 }
997
998
999 /*
1000   we can't return a crypt() password
1001 */
1002 static char *wb_aix_getpasswd(char *user)
1003 {
1004         logit("getpasswd '%s'\n", user);
1005         errno = ENOSYS;
1006         return NULL;
1007 }
1008
1009 /*
1010   this is called to update things like the last login time. We don't
1011   currently pass this onto the DC
1012 */
1013 static int wb_aix_putentry(char *key, char *table, char *attributes[],
1014                            attrval_t values[], int size)
1015 {
1016         logit("putentry key='%s' table='%s' attrib='%s'\n",
1017               key, table, size>=1?attributes[0]:"<null>");
1018         errno = ENOSYS;
1019         return -1;
1020 }
1021
1022 static int wb_aix_commit(char *key, char *table)
1023 {
1024         logit("commit key='%s' table='%s'\n");
1025         errno = ENOSYS;
1026         return -1;
1027 }
1028
1029 static int wb_aix_getgrusers(char *group, void *result, int type, int *size)
1030 {
1031         logit("getgrusers group='%s'\n", group);
1032         errno = ENOSYS;
1033         return -1;
1034 }
1035
1036
1037 #define DECL_METHOD(x) \
1038 int method_ ## x(void) \
1039 { \
1040         logit("UNIMPLEMENTED METHOD '%s'\n", #x); \
1041         errno = EINVAL; \
1042         return -1; \
1043 }
1044
1045 #if LOG_UNIMPLEMENTED_CALLS
1046 DECL_METHOD(delgroup);
1047 DECL_METHOD(deluser);
1048 DECL_METHOD(newgroup);
1049 DECL_METHOD(newuser);
1050 DECL_METHOD(putgrent);
1051 DECL_METHOD(putgrusers);
1052 DECL_METHOD(putpwent);
1053 DECL_METHOD(lock);
1054 DECL_METHOD(unlock);
1055 DECL_METHOD(getcred);
1056 DECL_METHOD(setcred);
1057 DECL_METHOD(deletecred);
1058 #endif
1059
1060 int wb_aix_init(struct secmethod_table *methods)
1061 {
1062         ZERO_STRUCTP(methods);
1063
1064 #ifdef HAVE_STRUCT_SECMETHOD_TABLE_METHOD_VERSION
1065         methods->method_version = SECMETHOD_VERSION_520;
1066 #endif
1067
1068         methods->method_getgrgid           = wb_aix_getgrgid;
1069         methods->method_getgrnam           = wb_aix_getgrnam;
1070         methods->method_getgrset           = wb_aix_getgrset;
1071         methods->method_getpwnam           = wb_aix_getpwnam;
1072         methods->method_getpwuid           = wb_aix_getpwuid;
1073         methods->method_getentry           = wb_aix_getentry;
1074         methods->method_open               = wb_aix_open;
1075         methods->method_close              = wb_aix_close;
1076         methods->method_normalize          = wb_aix_normalize;
1077         methods->method_passwdexpired      = wb_aix_passwdexpired;
1078         methods->method_putentry           = wb_aix_putentry;
1079         methods->method_getpasswd          = wb_aix_getpasswd;
1080         methods->method_authenticate       = wb_aix_authenticate;
1081         methods->method_commit             = wb_aix_commit;
1082         methods->method_chpass             = wb_aix_chpass;
1083         methods->method_passwdrestrictions = wb_aix_passwdrestrictions;
1084         methods->method_getgracct          = wb_aix_getgracct;
1085         methods->method_getgrusers         = wb_aix_getgrusers;
1086 #ifdef HAVE_STRUCT_SECMETHOD_TABLE_METHOD_ATTRLIST
1087         methods->method_attrlist           = wb_aix_attrlist;
1088 #endif
1089
1090 #if LOG_UNIMPLEMENTED_CALLS
1091         methods->method_delgroup      = method_delgroup;
1092         methods->method_deluser       = method_deluser;
1093         methods->method_newgroup      = method_newgroup;
1094         methods->method_newuser       = method_newuser;
1095         methods->method_putgrent      = method_putgrent;
1096         methods->method_putgrusers    = method_putgrusers;
1097         methods->method_putpwent      = method_putpwent;
1098         methods->method_lock          = method_lock;
1099         methods->method_unlock        = method_unlock;
1100         methods->method_getcred       = method_getcred;
1101         methods->method_setcred       = method_setcred;
1102         methods->method_deletecred    = method_deletecred;
1103 #endif
1104
1105         return AUTH_SUCCESS;
1106 }