winbind_nss_aix: use WBFLAG_FROM_NSS
[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_request request = {
273                 .wb_flags = WBFLAG_FROM_NSS,
274         };
275         struct winbindd_response response = {
276                 .length = 0,
277         };
278         struct group *grp;
279         NSS_STATUS ret;
280
281         logit("getgrgid %d\n", gid);
282
283         request.data.gid = gid;
284
285         ret = winbindd_request_response(NULL, WINBINDD_GETGRGID,
286                                         &request, &response);
287
288         logit("getgrgid ret=%d\n", ret);
289
290         HANDLE_ERRORS(ret);
291
292         grp = fill_grent(&response.data.gr, response.extra_data.data);
293
294         winbindd_free_response(&response);
295
296         return grp;
297 }
298
299 /* take a group name and return a filled struct group */
300 static struct group *wb_aix_getgrnam(const char *name)
301 {
302         struct winbindd_request request = {
303                 .wb_flags = WBFLAG_FROM_NSS,
304         };
305         struct winbindd_response response = {
306                 .length = 0,
307         };
308         NSS_STATUS ret;
309         struct group *grp;
310
311         if (*name == WB_AIX_ENCODED) {
312                 return wb_aix_getgrgid(decode_id(name));
313         }
314
315         logit("getgrnam '%s'\n", name);
316
317         STRCPY_RETNULL(request.data.groupname, name);
318
319         ret = winbindd_request_response(NULL, WINBINDD_GETGRNAM,
320                                         &request, &response);
321
322         HANDLE_ERRORS(ret);
323
324         grp = fill_grent(&response.data.gr, response.extra_data.data);
325
326         winbindd_free_response(&response);
327
328         return grp;
329 }
330
331
332 /* this call doesn't have to fill in the gr_mem, but we do anyway
333    for simplicity */
334 static struct group *wb_aix_getgracct(void *id, int type)
335 {
336         if (type == 1) {
337                 return wb_aix_getgrnam((char *)id);
338         }
339         if (type == 0) {
340                 return wb_aix_getgrgid(*(int *)id);
341         }
342         errno = EINVAL;
343         return NULL;
344 }
345
346
347 /* take a username and return a string containing a comma-separated
348    list of group id numbers to which the user belongs */
349 static char *wb_aix_getgrset(char *user)
350 {
351         struct winbindd_request request = {
352                 .wb_flags = WBFLAG_FROM_NSS,
353         };
354         struct winbindd_response response = {
355                 .length = 0,
356         };
357         NSS_STATUS ret;
358         int i, idx;
359         char *tmpbuf;
360         int num_gids;
361         gid_t *gid_list;
362         char *r_user = user;
363
364         if (*user == WB_AIX_ENCODED) {
365                 r_user = decode_user(r_user);
366                 if (!r_user) {
367                         errno = ENOENT;
368                         return NULL;
369                 }
370         }
371
372         logit("getgrset '%s'\n", r_user);
373
374         STRCPY_RETNULL(request.data.username, r_user);
375
376         if (*user == WB_AIX_ENCODED) {
377                 free(r_user);
378         }
379
380         ret = winbindd_request_response(NULL, WINBINDD_GETGROUPS,
381                                         &request, &response);
382
383         HANDLE_ERRORS(ret);
384
385         num_gids = response.data.num_entries;
386         gid_list = (gid_t *)response.extra_data.data;
387
388         /* allocate a space large enough to contruct the string */
389         tmpbuf = malloc(num_gids*12);
390         if (!tmpbuf) {
391                 return NULL;
392         }
393
394         for (idx=i=0; i < num_gids-1; i++) {
395                 idx += sprintf(tmpbuf+idx, "%u,", gid_list[i]);
396         }
397         idx += sprintf(tmpbuf+idx, "%u", gid_list[i]);
398
399         winbindd_free_response(&response);
400
401         return tmpbuf;
402 }
403
404
405 /* take a uid and return a filled struct passwd */
406 static struct passwd *wb_aix_getpwuid(uid_t uid)
407 {
408         struct winbindd_request request = {
409                 .wb_flags = WBFLAG_FROM_NSS,
410         };
411         struct winbindd_response response = {
412                 .length = 0,
413         };
414         NSS_STATUS ret;
415         struct passwd *pwd;
416
417         logit("getpwuid '%d'\n", uid);
418
419         request.data.uid = uid;
420
421         ret = winbindd_request_response(NULL, WINBINDD_GETPWUID,
422                                         &request, &response);
423
424         HANDLE_ERRORS(ret);
425
426         pwd = fill_pwent(&response.data.pw);
427
428         winbindd_free_response(&response);
429
430         logit("getpwuid gave ptr %p\n", pwd);
431
432         return pwd;
433 }
434
435
436 /* take a username and return a filled struct passwd */
437 static struct passwd *wb_aix_getpwnam(const char *name)
438 {
439         struct winbindd_request request = {
440                 .wb_flags = WBFLAG_FROM_NSS,
441         };
442         struct winbindd_response response = {
443                 .length = 0,
444         };
445         NSS_STATUS ret;
446         struct passwd *pwd;
447
448         if (*name == WB_AIX_ENCODED) {
449                 return wb_aix_getpwuid(decode_id(name));
450         }
451
452         logit("getpwnam '%s'\n", name);
453
454         STRCPY_RETNULL(request.data.username, name);
455
456         ret = winbindd_request_response(NULL, WINBINDD_GETPWNAM,
457                                         &request, &response);
458
459         HANDLE_ERRORS(ret);
460
461         pwd = fill_pwent(&response.data.pw);
462
463         winbindd_free_response(&response);
464
465         logit("getpwnam gave ptr %p\n", pwd);
466
467         return pwd;
468 }
469
470 /*
471   list users
472 */
473 static int wb_aix_lsuser(char *attributes[], attrval_t results[], int size)
474 {
475         NSS_STATUS ret;
476         struct winbindd_request request = {
477                 .wb_flags = WBFLAG_FROM_NSS,
478         };
479         struct winbindd_response response = {
480                 .length = 0,
481         };
482         int len;
483         char *s;
484
485         if (size != 1 || strcmp(attributes[0], S_USERS) != 0) {
486                 logit("invalid lsuser op\n");
487                 errno = EINVAL;
488                 return -1;
489         }
490
491         ret = winbindd_request_response(NULL, WINBINDD_LIST_USERS,
492                                         &request, &response);
493         if (ret != 0) {
494                 errno = EINVAL;
495                 return -1;
496         }
497
498         len = strlen(response.extra_data.data);
499
500         s = malloc(len+2);
501         if (!s) {
502                 winbindd_free_response(&response);
503                 errno = ENOMEM;
504                 return -1;
505         }
506
507         memcpy(s, response.extra_data.data, len+1);
508
509         replace_commas(s);
510
511         results[0].attr_un.au_char = s;
512         results[0].attr_flag = 0;
513
514         winbindd_free_response(&response);
515
516         return 0;
517 }
518
519
520 /*
521   list groups
522 */
523 static int wb_aix_lsgroup(char *attributes[], attrval_t results[], int size)
524 {
525         NSS_STATUS ret;
526         struct winbindd_request request = {
527                 .wb_flags = WBFLAG_FROM_NSS,
528         };
529         struct winbindd_response response = {
530                 .length = 0,
531         };
532         int len;
533         char *s;
534
535         if (size != 1 || strcmp(attributes[0], S_GROUPS) != 0) {
536                 logit("invalid lsgroup op\n");
537                 errno = EINVAL;
538                 return -1;
539         }
540
541         ret = winbindd_request_response(NULL, WINBINDD_LIST_GROUPS,
542                                         &request, &response);
543         if (ret != 0) {
544                 errno = EINVAL;
545                 return -1;
546         }
547
548         len = strlen(response.extra_data.data);
549
550         s = malloc(len+2);
551         if (!s) {
552                 winbindd_free_response(&response);
553                 errno = ENOMEM;
554                 return -1;
555         }
556
557         memcpy(s, response.extra_data.data, len+1);
558
559         replace_commas(s);
560
561         results[0].attr_un.au_char = s;
562         results[0].attr_flag = 0;
563
564         winbindd_free_response(&response);
565
566         return 0;
567 }
568
569
570 static attrval_t pwd_to_group(struct passwd *pwd)
571 {
572         attrval_t r;
573         struct group *grp = wb_aix_getgrgid(pwd->pw_gid);
574
575         if (!grp) {
576                 r.attr_flag = EINVAL;
577         } else {
578                 r.attr_flag = 0;
579                 r.attr_un.au_char = strdup(grp->gr_name);
580                 free_grp(grp);
581         }
582
583         return r;
584 }
585
586 static attrval_t pwd_to_groupsids(struct passwd *pwd)
587 {
588         attrval_t r;
589         char *s, *p;
590         size_t mlen;
591
592         if ( (s = wb_aix_getgrset(pwd->pw_name)) == NULL ) {
593                 r.attr_flag = EINVAL;
594                 return r;
595         }
596
597         mlen = strlen(s)+2;
598         if ( (p = malloc(mlen)) == NULL ) {
599                 r.attr_flag = ENOMEM;
600                 return r;
601         }
602
603         strncpy(p, s, mlen);
604         p[mlen-1] = '\0';
605         replace_commas(p);
606         free(s);
607
608         r.attr_un.au_char = p;
609
610         return r;
611 }
612
613 static attrval_t pwd_to_sid(struct passwd *pwd)
614 {
615         char buf[(1 /* U/G */ + 10 /* 2^32 */ + 1 /* \n */) + 1] = { 0, };
616         int len;
617         struct winbindd_request request;
618         struct winbindd_response response;
619         NSS_STATUS result;
620         attrval_t r = {
621                 .attr_flag = ENOENT,
622         };
623
624         len = snprintf(buf, sizeof(buf),
625                        "U%"PRIu32"\n",
626                        (uint32_t)pwd->pw_uid);
627         if (len >= sizeof(buf)) {
628                 r = (attrval_t) {
629                         .attr_flag = EINVAL,
630                 };
631                 return r;
632         }
633
634         request = (struct winbindd_request) {
635                 .wb_flags = WBFLAG_FROM_NSS,
636                 .extra_data.data = buf,
637                 .extra_len = strlen(buf)+1,
638         };
639         response = (struct winbindd_response) {
640                 .length = 0,
641         };
642
643         result = winbindd_request_response(NULL, WINBINDD_XIDS_TO_SIDS,
644                                            &request, &response);
645         if (result == NSS_STATUS_SUCCESS) {
646                 r.attr_flag = 0;
647                 r.attr_un.au_char = strdup(response.data.sid.sid);
648         }
649
650         return r;
651 }
652
653 static int wb_aix_user_attrib(const char *key, char *attributes[],
654                               attrval_t results[], int size)
655 {
656         struct passwd *pwd;
657         int i;
658
659         pwd = wb_aix_getpwnam(key);
660         if (!pwd) {
661                 errno = ENOENT;
662                 return -1;
663         }
664
665         for (i=0;i<size;i++) {
666                 results[i].attr_flag = 0;
667
668                 if (strcmp(attributes[i], S_ID) == 0) {
669                         results[i].attr_un.au_int = pwd->pw_uid;
670 #ifdef _AIXVERSION_530
671                 } else if (strcmp(attributes[i], S_PGID) == 0) {
672                         results[i].attr_un.au_int = pwd->pw_gid;
673 #endif
674                 } else if (strcmp(attributes[i], S_PWD) == 0) {
675                         results[i].attr_un.au_char = strdup(pwd->pw_passwd);
676                 } else if (strcmp(attributes[i], S_HOME) == 0) {
677                         results[i].attr_un.au_char = strdup(pwd->pw_dir);
678                 } else if (strcmp(attributes[i], S_SHELL) == 0) {
679                         results[i].attr_un.au_char = strdup(pwd->pw_shell);
680                 } else if (strcmp(attributes[i], S_REGISTRY) == 0) {
681                         results[i].attr_un.au_char = strdup("WINBIND");
682                 } else if (strcmp(attributes[i], S_GECOS) == 0) {
683                         results[i].attr_un.au_char = strdup(pwd->pw_gecos);
684                 } else if (strcmp(attributes[i], S_PGRP) == 0) {
685                         results[i] = pwd_to_group(pwd);
686                 } else if (strcmp(attributes[i], S_GROUPS) == 0) {
687                         results[i] = pwd_to_groupsids(pwd);
688                 } else if (strcmp(attributes[i], S_GROUPSIDS) == 0) {
689                         results[i] = pwd_to_groupsids(pwd);
690                 } else if (strcmp(attributes[i], "SID") == 0) {
691                         results[i] = pwd_to_sid(pwd);
692                 } else {
693                         logit("Unknown user attribute '%s'\n", attributes[i]);
694                         results[i].attr_flag = EINVAL;
695                 }
696         }
697
698         free_pwd(pwd);
699
700         return 0;
701 }
702
703 static int wb_aix_group_attrib(const char *key, char *attributes[],
704                                attrval_t results[], int size)
705 {
706         struct group *grp;
707         int i;
708
709         grp = wb_aix_getgrnam(key);
710         if (!grp) {
711                 errno = ENOENT;
712                 return -1;
713         }
714
715         for (i=0;i<size;i++) {
716                 results[i].attr_flag = 0;
717
718                 if (strcmp(attributes[i], S_PWD) == 0) {
719                         results[i].attr_un.au_char = strdup(grp->gr_passwd);
720                 } else if (strcmp(attributes[i], S_ID) == 0) {
721                         results[i].attr_un.au_int = grp->gr_gid;
722                 } else {
723                         logit("Unknown group attribute '%s'\n", attributes[i]);
724                         results[i].attr_flag = EINVAL;
725                 }
726         }
727
728         free_grp(grp);
729
730         return 0;
731 }
732
733
734 /*
735   called for user/group enumerations
736 */
737 static int wb_aix_getentry(char *key, char *table, char *attributes[],
738                            attrval_t results[], int size)
739 {
740         logit("Got getentry with key='%s' table='%s' size=%d attributes[0]='%s'\n",
741               key, table, size, attributes[0]);
742
743         if (strcmp(key, "ALL") == 0 &&
744             strcmp(table, "user") == 0) {
745                 return wb_aix_lsuser(attributes, results, size);
746         }
747
748         if (strcmp(key, "ALL") == 0 &&
749             strcmp(table, "group") == 0) {
750                 return wb_aix_lsgroup(attributes, results, size);
751         }
752
753         if (strcmp(table, "user") == 0) {
754                 return wb_aix_user_attrib(key, attributes, results, size);
755         }
756
757         if (strcmp(table, "group") == 0) {
758                 return wb_aix_group_attrib(key, attributes, results, size);
759         }
760
761         logit("Unknown getentry operation key='%s' table='%s'\n", key, table);
762
763         errno = ENOSYS;
764         return -1;
765 }
766
767
768
769 /*
770   called to start the backend
771 */
772 static void *wb_aix_open(const char *name, const char *domain, int mode, char *options)
773 {
774         if (strstr(options, "debug")) {
775                 debug_enabled = 1;
776         }
777         logit("open name='%s' mode=%d domain='%s' options='%s'\n", name, domain,
778               mode, options);
779         return NULL;
780 }
781
782 static void wb_aix_close(void *token)
783 {
784         logit("close\n");
785         return;
786 }
787
788 #ifdef HAVE_STRUCT_SECMETHOD_TABLE_METHOD_ATTRLIST
789 /*
790    return a list of additional attributes supported by the backend
791 */
792 static attrlist_t **wb_aix_attrlist(void)
793 {
794         /* pretty confusing but we are allocating the array of pointers
795            and the structures we'll be pointing to all at once.  So
796            you need N+1 pointers and N structures. */
797
798         attrlist_t **ret = NULL;
799         attrlist_t *offset = NULL;
800         int i;
801         int n;
802         size_t size;
803
804         struct attr_types {
805                 const char *name;
806                 int flags;
807                 int type;
808         } attr_list[] = {
809                 /* user attributes */
810                 {S_ID,          AL_USERATTR,    SEC_INT},
811                 {S_PGRP,        AL_USERATTR,    SEC_CHAR},
812                 {S_HOME,        AL_USERATTR,    SEC_CHAR},
813                 {S_SHELL,       AL_USERATTR,    SEC_CHAR},
814 #ifdef _AIXVERSION_530
815                 {S_PGID,        AL_USERATTR,    SEC_INT},
816 #endif
817                 {S_GECOS,       AL_USERATTR,    SEC_CHAR},
818                 {S_SHELL,       AL_USERATTR,    SEC_CHAR},
819                 {S_PGRP,        AL_USERATTR,    SEC_CHAR},
820                 {S_GROUPS,      AL_USERATTR,    SEC_LIST},
821                 {S_GROUPSIDS,   AL_USERATTR,    SEC_LIST},
822                 {"SID",         AL_USERATTR,    SEC_CHAR},
823
824                 /* group attributes */
825                 {S_ID,          AL_GROUPATTR,   SEC_INT}
826         };
827
828         logit("method attrlist called\n");
829
830         n = sizeof(attr_list) / sizeof(struct attr_types);
831         size = (n*sizeof(attrlist_t *));
832
833         if ( (ret = malloc( size )) == NULL ) {
834                 errno = ENOMEM;
835                 return NULL;
836         }
837
838         /* offset to where the structures start in the buffer */
839
840         offset = (attrlist_t *)(ret + n);
841
842         /* now loop over the user_attr_list[] array and add
843            all the members */
844
845         for ( i=0; i<n; i++ ) {
846                 attrlist_t *a = malloc(sizeof(attrlist_t));
847
848                 if ( !a ) {
849                         /* this is bad.  Just bail */
850                         return NULL;
851                 }
852
853                 a->al_name  = strdup(attr_list[i].name);
854                 a->al_flags = attr_list[i].flags;
855                 a->al_type  = attr_list[i].type;
856
857                 ret[i] = a;
858         }
859         ret[n] = NULL;
860
861         return ret;
862 }
863 #endif
864
865
866 /*
867   turn a long username into a short one. Needed to cope with the 8 char
868   username limit in AIX 5.2 and below
869 */
870 static int wb_aix_normalize(char *longname, char *shortname)
871 {
872         struct passwd *pwd;
873
874         logit("normalize '%s'\n", longname);
875
876         /* automatically cope with AIX 5.3 with longer usernames
877            when it comes out */
878         if (S_NAMELEN > strlen(longname)) {
879                 strncpy(shortname, longname, S_NAMELEN);
880                 shortname[S_NAMELEN-1] = '\0';
881                 return 1;
882         }
883
884         pwd = wb_aix_getpwnam(longname);
885         if (!pwd) {
886                 errno = ENOENT;
887                 return 0;
888         }
889
890         sprintf(shortname, "%c%07u", WB_AIX_ENCODED, pwd->pw_uid);
891
892         free_pwd(pwd);
893
894         return 1;
895 }
896
897
898 /*
899   authenticate a user
900  */
901 static int wb_aix_authenticate(char *user, char *pass,
902                                int *reenter, char **message)
903 {
904         struct winbindd_request request = {
905                 .wb_flags = WBFLAG_FROM_NSS,
906         };
907         struct winbindd_response response = {
908                 .length = 0,
909         };
910         NSS_STATUS result;
911         char *r_user = user;
912
913         logit("authenticate '%s' response='%s'\n", user, pass);
914
915         *reenter = 0;
916         *message = NULL;
917
918         /* Send off request */
919         if (*user == WB_AIX_ENCODED) {
920                 r_user = decode_user(r_user);
921                 if (!r_user) {
922                         return AUTH_NOTFOUND;
923                 }
924         }
925
926         STRCPY_RET(request.data.auth.user, r_user);
927         STRCPY_RET(request.data.auth.pass, pass);
928
929         if (*user == WB_AIX_ENCODED) {
930                 free(r_user);
931         }
932
933         result = winbindd_request_response(NULL, WINBINDD_PAM_AUTH,
934                                            &request, &response);
935
936         winbindd_free_response(&response);
937
938         logit("auth result %d for '%s'\n", result, user);
939
940         if (result == NSS_STATUS_SUCCESS) {
941                 errno = 0;
942                 return AUTH_SUCCESS;
943         }
944
945         return AUTH_FAILURE;
946 }
947
948
949 /*
950   change a user password
951 */
952 static int wb_aix_chpass(char *user, char *oldpass, char *newpass, char **message)
953 {
954         struct winbindd_request request = {
955                 .wb_flags = WBFLAG_FROM_NSS,
956         };
957         struct winbindd_response response = {
958                 .length = 0,
959         };
960         NSS_STATUS result;
961         char *r_user = user;
962
963         if (*user == WB_AIX_ENCODED) {
964                 r_user = decode_user(r_user);
965                 if (!r_user) {
966                         errno = ENOENT;
967                         return -1;
968                 }
969         }
970
971         logit("chpass '%s' old='%s' new='%s'\n", r_user, oldpass, newpass);
972
973         *message = NULL;
974
975         /* Send off request */
976         STRCPY_RET(request.data.chauthtok.user, r_user);
977         STRCPY_RET(request.data.chauthtok.oldpass, oldpass);
978         STRCPY_RET(request.data.chauthtok.newpass, newpass);
979
980         if (*user == WB_AIX_ENCODED) {
981                 free(r_user);
982         }
983
984         result = winbindd_request_response(NULL, WINBINDD_PAM_CHAUTHTOK,
985                                            &request, &response);
986
987         winbindd_free_response(&response);
988
989         if (result == NSS_STATUS_SUCCESS) {
990                 errno = 0;
991                 return 0;
992         }
993
994         errno = EINVAL;
995         return -1;
996 }
997
998 /*
999   don't do any password strength testing for now
1000 */
1001 static int wb_aix_passwdrestrictions(char *user, char *newpass, char *oldpass,
1002                                      char **message)
1003 {
1004         logit("passwdresrictions called for '%s'\n", user);
1005         return 0;
1006 }
1007
1008
1009 static int wb_aix_passwdexpired(char *user, char **message)
1010 {
1011         logit("passwdexpired '%s'\n", user);
1012         /* we should check the account bits here */
1013         return 0;
1014 }
1015
1016
1017 /*
1018   we can't return a crypt() password
1019 */
1020 static char *wb_aix_getpasswd(char *user)
1021 {
1022         logit("getpasswd '%s'\n", user);
1023         errno = ENOSYS;
1024         return NULL;
1025 }
1026
1027 /*
1028   this is called to update things like the last login time. We don't
1029   currently pass this onto the DC
1030 */
1031 static int wb_aix_putentry(char *key, char *table, char *attributes[],
1032                            attrval_t values[], int size)
1033 {
1034         logit("putentry key='%s' table='%s' attrib='%s'\n",
1035               key, table, size>=1?attributes[0]:"<null>");
1036         errno = ENOSYS;
1037         return -1;
1038 }
1039
1040 static int wb_aix_commit(char *key, char *table)
1041 {
1042         logit("commit key='%s' table='%s'\n");
1043         errno = ENOSYS;
1044         return -1;
1045 }
1046
1047 static int wb_aix_getgrusers(char *group, void *result, int type, int *size)
1048 {
1049         logit("getgrusers group='%s'\n", group);
1050         errno = ENOSYS;
1051         return -1;
1052 }
1053
1054
1055 #define DECL_METHOD(x) \
1056 int method_ ## x(void) \
1057 { \
1058         logit("UNIMPLEMENTED METHOD '%s'\n", #x); \
1059         errno = EINVAL; \
1060         return -1; \
1061 }
1062
1063 #if LOG_UNIMPLEMENTED_CALLS
1064 DECL_METHOD(delgroup);
1065 DECL_METHOD(deluser);
1066 DECL_METHOD(newgroup);
1067 DECL_METHOD(newuser);
1068 DECL_METHOD(putgrent);
1069 DECL_METHOD(putgrusers);
1070 DECL_METHOD(putpwent);
1071 DECL_METHOD(lock);
1072 DECL_METHOD(unlock);
1073 DECL_METHOD(getcred);
1074 DECL_METHOD(setcred);
1075 DECL_METHOD(deletecred);
1076 #endif
1077
1078 int wb_aix_init(struct secmethod_table *methods)
1079 {
1080         ZERO_STRUCTP(methods);
1081
1082 #ifdef HAVE_STRUCT_SECMETHOD_TABLE_METHOD_VERSION
1083         methods->method_version = SECMETHOD_VERSION_520;
1084 #endif
1085
1086         methods->method_getgrgid           = wb_aix_getgrgid;
1087         methods->method_getgrnam           = wb_aix_getgrnam;
1088         methods->method_getgrset           = wb_aix_getgrset;
1089         methods->method_getpwnam           = wb_aix_getpwnam;
1090         methods->method_getpwuid           = wb_aix_getpwuid;
1091         methods->method_getentry           = wb_aix_getentry;
1092         methods->method_open               = wb_aix_open;
1093         methods->method_close              = wb_aix_close;
1094         methods->method_normalize          = wb_aix_normalize;
1095         methods->method_passwdexpired      = wb_aix_passwdexpired;
1096         methods->method_putentry           = wb_aix_putentry;
1097         methods->method_getpasswd          = wb_aix_getpasswd;
1098         methods->method_authenticate       = wb_aix_authenticate;
1099         methods->method_commit             = wb_aix_commit;
1100         methods->method_chpass             = wb_aix_chpass;
1101         methods->method_passwdrestrictions = wb_aix_passwdrestrictions;
1102         methods->method_getgracct          = wb_aix_getgracct;
1103         methods->method_getgrusers         = wb_aix_getgrusers;
1104 #ifdef HAVE_STRUCT_SECMETHOD_TABLE_METHOD_ATTRLIST
1105         methods->method_attrlist           = wb_aix_attrlist;
1106 #endif
1107
1108 #if LOG_UNIMPLEMENTED_CALLS
1109         methods->method_delgroup      = method_delgroup;
1110         methods->method_deluser       = method_deluser;
1111         methods->method_newgroup      = method_newgroup;
1112         methods->method_newuser       = method_newuser;
1113         methods->method_putgrent      = method_putgrent;
1114         methods->method_putgrusers    = method_putgrusers;
1115         methods->method_putpwent      = method_putpwent;
1116         methods->method_lock          = method_lock;
1117         methods->method_unlock        = method_unlock;
1118         methods->method_getcred       = method_getcred;
1119         methods->method_setcred       = method_setcred;
1120         methods->method_deletecred    = method_deletecred;
1121 #endif
1122
1123         return AUTH_SUCCESS;
1124 }