2 Unix SMB/CIFS implementation.
4 AIX loadable authentication module, providing identification and
5 authentication routines against Samba winbind/Windows NT Domain
7 Copyright (C) Tim Potter 2003
8 Copyright (C) Steve Roylance 2003
9 Copyright (C) Andrew Tridgell 2003-2004
11 This library is free software; you can redistribute it and/or
12 modify it under the terms of the GNU Library General Public
13 License as published by the Free Software Foundation; either
14 version 2 of the License, or (at your option) any later version.
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.
21 You should have received a copy of the GNU Library General Public
22 License along with this library; if not, write to the
23 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 Boston, MA 02111-1307, USA.
29 To install this module copy nsswitch/WINBIND to /usr/lib/security and add
30 "WINBIND" in /usr/lib/security/methods.cfg and /etc/security/user
32 Note that this module also provides authentication and password
33 changing routines, so you do not need to install the winbind PAM
37 http://publib16.boulder.ibm.com/doc_link/en_US/a_doc_lib/aixprggd/kernextc/sec_load_mod.htm
38 for some information in the interface that this module implements
40 Many thanks to Julianne Haugh for explaining some of the finer
41 details of this interface.
43 To debug this module use uess_test.c (which you can get from tridge)
44 or set "options=debug" in /usr/lib/security/methods.cfg
54 #include "winbind_client.h"
56 #define WB_AIX_ENCODED '_'
58 static int debug_enabled;
61 static void logit(const char *format, ...)
68 f = fopen("/tmp/WINBIND_DEBUG.log", "a");
71 vfprintf(f, format, ap);
77 #define HANDLE_ERRORS(ret) do { \
78 if ((ret) == NSS_STATUS_NOTFOUND) { \
81 } else if ((ret) != NSS_STATUS_SUCCESS) { \
87 #define STRCPY_RET(dest, src) \
89 if (strlen(src)+1 > sizeof(dest)) { errno = EINVAL; return -1; } \
93 #define STRCPY_RETNULL(dest, src) \
95 if (strlen(src)+1 > sizeof(dest)) { errno = EINVAL; return NULL; } \
100 /* free a passwd structure */
101 static void free_pwd(struct passwd *pwd)
104 free(pwd->pw_passwd);
111 /* free a group structure */
112 static void free_grp(struct group *grp)
117 free(grp->gr_passwd);
124 for (i=0; grp->gr_mem[i]; i++) {
125 free(grp->gr_mem[i]);
133 /* replace commas with nulls, and null terminate */
134 static void replace_commas(char *s)
137 for (p=strchr(s, ','); p; p = strchr(p+1, ',')) {
142 p0[strlen(p0)+1] = 0;
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'
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
155 static unsigned decode_id(const char *name)
158 sscanf(name+1, "%u", &id);
162 static struct passwd *wb_aix_getpwuid(uid_t uid);
164 static char *decode_user(const char *name)
170 sscanf(name+1, "%u", &id);
171 pwd = wb_aix_getpwuid(id);
175 ret = strdup(pwd->pw_name);
179 logit("decoded '%s' -> '%s'\n", name, ret);
186 fill a struct passwd from a winbindd_pw struct, allocating as a single block
188 static struct passwd *fill_pwent(struct winbindd_pw *pw)
190 struct passwd *result;
192 result = calloc(1, sizeof(struct passwd));
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);
211 fill a struct group from a winbindd_pw struct, allocating as a single block
213 static struct group *fill_grent(struct winbindd_gr *gr, char *gr_mem)
216 struct group *result;
219 result = calloc(1, sizeof(struct group));
225 result->gr_gid = gr->gr_gid;
227 result->gr_name = strdup(gr->gr_name);
228 result->gr_passwd = strdup(gr->gr_passwd);
230 /* Group membership */
231 if ((gr->num_gr_mem < 0) || !gr_mem) {
235 if (gr->num_gr_mem == 0) {
240 result->gr_mem = (char **)malloc(sizeof(char *) * (gr->num_gr_mem+1));
241 if (!result->gr_mem) {
246 /* Start looking at extra data */
248 for (name = strtok_r(gr_mem, ",", &p);
250 name = strtok_r(NULL, ",", &p)) {
251 if (i == gr->num_gr_mem) {
254 result->gr_mem[i] = strdup(name);
259 result->gr_mem[i] = NULL;
266 /* take a group id and return a filled struct group */
267 static struct group *wb_aix_getgrgid(gid_t gid)
269 struct winbindd_response response;
270 struct winbindd_request request;
274 logit("getgrgid %d\n", gid);
276 ZERO_STRUCT(response);
277 ZERO_STRUCT(request);
279 request.data.gid = gid;
281 ret = winbindd_request_response(WINBINDD_GETGRGID, &request, &response);
283 logit("getgrgid ret=%d\n", ret);
287 grp = fill_grent(&response.data.gr, response.extra_data);
289 free_response(&response);
294 /* take a group name and return a filled struct group */
295 static struct group *wb_aix_getgrnam(const char *name)
297 struct winbindd_response response;
298 struct winbindd_request request;
302 if (*name == WB_AIX_ENCODED) {
303 return wb_aix_getgrgid(decode_id(name));
306 logit("getgrnam '%s'\n", name);
308 ZERO_STRUCT(response);
309 ZERO_STRUCT(request);
311 STRCPY_RETNULL(request.data.groupname, name);
313 ret = winbindd_request_response(WINBINDD_GETGRNAM, &request, &response);
317 grp = fill_grent(&response.data.gr, response.extra_data);
319 free_response(&response);
325 /* this call doesn't have to fill in the gr_mem, but we do anyway
327 static struct group *wb_aix_getgracct(void *id, int type)
330 return wb_aix_getgrnam((char *)id);
333 return wb_aix_getgrgid(*(int *)id);
340 /* take a username and return a string containing a comma-separated
341 list of group id numbers to which the user belongs */
342 static char *wb_aix_getgrset(char *user)
344 struct winbindd_response response;
345 struct winbindd_request request;
353 if (*user == WB_AIX_ENCODED) {
354 r_user = decode_user(r_user);
361 logit("getgrset '%s'\n", r_user);
363 STRCPY_RETNULL(request.data.username, r_user);
365 if (*user == WB_AIX_ENCODED) {
369 ret = winbindd_request_response(WINBINDD_GETGROUPS, &request, &response);
373 num_gids = response.data.num_entries;
374 gid_list = (gid_t *)response.extra_data;
376 /* allocate a space large enough to contruct the string */
377 tmpbuf = malloc(num_gids*12);
382 for (idx=i=0; i < num_gids-1; i++) {
383 idx += sprintf(tmpbuf+idx, "%u,", gid_list[i]);
385 idx += sprintf(tmpbuf+idx, "%u", gid_list[i]);
387 free_response(&response);
393 /* take a uid and return a filled struct passwd */
394 static struct passwd *wb_aix_getpwuid(uid_t uid)
396 struct winbindd_response response;
397 struct winbindd_request request;
401 logit("getpwuid '%d'\n", uid);
403 ZERO_STRUCT(response);
404 ZERO_STRUCT(request);
406 request.data.uid = uid;
408 ret = winbindd_request_response(WINBINDD_GETPWUID, &request, &response);
412 pwd = fill_pwent(&response.data.pw);
414 free_response(&response);
416 logit("getpwuid gave ptr %p\n", pwd);
422 /* take a username and return a filled struct passwd */
423 static struct passwd *wb_aix_getpwnam(const char *name)
425 struct winbindd_response response;
426 struct winbindd_request request;
430 if (*name == WB_AIX_ENCODED) {
431 return wb_aix_getpwuid(decode_id(name));
434 logit("getpwnam '%s'\n", name);
436 ZERO_STRUCT(response);
437 ZERO_STRUCT(request);
439 STRCPY_RETNULL(request.data.username, name);
441 ret = winbindd_request_response(WINBINDD_GETPWNAM, &request, &response);
445 pwd = fill_pwent(&response.data.pw);
447 free_response(&response);
449 logit("getpwnam gave ptr %p\n", pwd);
457 static int wb_aix_lsuser(char *attributes[], attrval_t results[], int size)
460 struct winbindd_request request;
461 struct winbindd_response response;
465 if (size != 1 || strcmp(attributes[0], S_USERS) != 0) {
466 logit("invalid lsuser op\n");
471 ZERO_STRUCT(request);
472 ZERO_STRUCT(response);
474 ret = winbindd_request_response(WINBINDD_LIST_USERS, &request, &response);
480 len = strlen(response.extra_data);
484 free_response(&response);
489 memcpy(s, response.extra_data, len+1);
493 results[0].attr_un.au_char = s;
494 results[0].attr_flag = 0;
496 free_response(&response);
505 static int wb_aix_lsgroup(char *attributes[], attrval_t results[], int size)
508 struct winbindd_request request;
509 struct winbindd_response response;
513 if (size != 1 || strcmp(attributes[0], S_GROUPS) != 0) {
514 logit("invalid lsgroup op\n");
519 ZERO_STRUCT(request);
520 ZERO_STRUCT(response);
522 ret = winbindd_request_response(WINBINDD_LIST_GROUPS, &request, &response);
528 len = strlen(response.extra_data);
532 free_response(&response);
537 memcpy(s, response.extra_data, len+1);
541 results[0].attr_un.au_char = s;
542 results[0].attr_flag = 0;
544 free_response(&response);
550 static attrval_t pwd_to_group(struct passwd *pwd)
553 struct group *grp = wb_aix_getgrgid(pwd->pw_gid);
556 r.attr_flag = EINVAL;
559 r.attr_un.au_char = strdup(grp->gr_name);
566 static attrval_t pwd_to_groupsids(struct passwd *pwd)
571 s = wb_aix_getgrset(pwd->pw_name);
573 r.attr_flag = EINVAL;
577 p = malloc(strlen(s)+2);
579 r.attr_flag = ENOMEM;
587 r.attr_un.au_char = p;
592 static attrval_t pwd_to_sid(struct passwd *pwd)
594 struct winbindd_request request;
595 struct winbindd_response response;
598 ZERO_STRUCT(request);
599 ZERO_STRUCT(response);
601 request.data.uid = pwd->pw_uid;
603 if (winbindd_request_response(WINBINDD_UID_TO_SID, &request, &response) !=
604 NSS_STATUS_SUCCESS) {
605 r.attr_flag = ENOENT;
608 r.attr_un.au_char = strdup(response.data.sid.sid);
614 static int wb_aix_user_attrib(const char *key, char *attributes[],
615 attrval_t results[], int size)
620 pwd = wb_aix_getpwnam(key);
626 for (i=0;i<size;i++) {
627 results[i].attr_flag = 0;
629 if (strcmp(attributes[i], S_ID) == 0) {
630 results[i].attr_un.au_int = pwd->pw_uid;
631 } else if (strcmp(attributes[i], S_PWD) == 0) {
632 results[i].attr_un.au_char = strdup(pwd->pw_passwd);
633 } else if (strcmp(attributes[i], S_HOME) == 0) {
634 results[i].attr_un.au_char = strdup(pwd->pw_dir);
635 } else if (strcmp(attributes[0], S_SHELL) == 0) {
636 results[i].attr_un.au_char = strdup(pwd->pw_shell);
637 } else if (strcmp(attributes[0], S_REGISTRY) == 0) {
638 results[i].attr_un.au_char = strdup("WINBIND");
639 } else if (strcmp(attributes[0], S_GECOS) == 0) {
640 results[i].attr_un.au_char = strdup(pwd->pw_gecos);
641 } else if (strcmp(attributes[0], S_PGRP) == 0) {
642 results[i] = pwd_to_group(pwd);
643 } else if (strcmp(attributes[0], S_GECOS) == 0) {
644 results[i].attr_un.au_char = strdup(pwd->pw_gecos);
645 } else if (strcmp(attributes[0], S_GROUPSIDS) == 0) {
646 results[i] = pwd_to_groupsids(pwd);
647 } else if (strcmp(attributes[0], "SID") == 0) {
648 results[i] = pwd_to_sid(pwd);
650 logit("Unknown user attribute '%s'\n", attributes[i]);
651 results[i].attr_flag = EINVAL;
660 static int wb_aix_group_attrib(const char *key, char *attributes[],
661 attrval_t results[], int size)
666 grp = wb_aix_getgrnam(key);
672 for (i=0;i<size;i++) {
673 results[i].attr_flag = 0;
675 if (strcmp(attributes[i], S_PWD) == 0) {
676 results[i].attr_un.au_char = strdup(grp->gr_passwd);
677 } else if (strcmp(attributes[i], S_ID) == 0) {
678 results[i].attr_un.au_int = grp->gr_gid;
680 logit("Unknown group attribute '%s'\n", attributes[i]);
681 results[i].attr_flag = EINVAL;
692 called for user/group enumerations
694 static int wb_aix_getentry(char *key, char *table, char *attributes[],
695 attrval_t results[], int size)
697 logit("Got getentry with key='%s' table='%s' size=%d attributes[0]='%s'\n",
698 key, table, size, attributes[0]);
700 if (strcmp(key, "ALL") == 0 &&
701 strcmp(table, "user") == 0) {
702 return wb_aix_lsuser(attributes, results, size);
705 if (strcmp(key, "ALL") == 0 &&
706 strcmp(table, "group") == 0) {
707 return wb_aix_lsgroup(attributes, results, size);
710 if (strcmp(table, "user") == 0) {
711 return wb_aix_user_attrib(key, attributes, results, size);
714 if (strcmp(table, "group") == 0) {
715 return wb_aix_group_attrib(key, attributes, results, size);
718 logit("Unknown getentry operation key='%s' table='%s'\n", key, table);
727 called to start the backend
729 static void *wb_aix_open(const char *name, const char *domain, int mode, char *options)
731 if (strstr(options, "debug")) {
734 logit("open name='%s' mode=%d domain='%s' options='%s'\n", name, domain,
739 static void wb_aix_close(void *token)
745 #ifdef HAVE_STRUCT_SECMETHOD_TABLE_METHOD_ATTRLIST
747 return a list of additional attributes supported by the backend
749 static attrlist_t **wb_aix_attrlist(void)
752 logit("method attrlist called\n");
753 ret = malloc(2*sizeof(attrlist_t *) + sizeof(attrlist_t));
759 ret[0] = (attrlist_t *)(ret+2);
761 /* just one extra attribute - the windows SID */
762 ret[0]->al_name = strdup("SID");
763 ret[0]->al_flags = AL_USERATTR;
764 ret[0]->al_type = SEC_CHAR;
773 turn a long username into a short one. Needed to cope with the 8 char
774 username limit in AIX 5.2 and below
776 static int wb_aix_normalize(char *longname, char *shortname)
780 logit("normalize '%s'\n", longname);
782 /* automatically cope with AIX 5.3 with longer usernames
784 if (S_NAMELEN > strlen(longname)) {
785 strcpy(shortname, longname);
789 pwd = wb_aix_getpwnam(longname);
795 sprintf(shortname, "%c%07u", WB_AIX_ENCODED, pwd->pw_uid);
806 static int wb_aix_authenticate(char *user, char *pass,
807 int *reenter, char **message)
809 struct winbindd_request request;
810 struct winbindd_response response;
814 logit("authenticate '%s' response='%s'\n", user, pass);
819 /* Send off request */
820 ZERO_STRUCT(request);
821 ZERO_STRUCT(response);
823 if (*user == WB_AIX_ENCODED) {
824 r_user = decode_user(r_user);
826 return AUTH_NOTFOUND;
830 STRCPY_RET(request.data.auth.user, r_user);
831 STRCPY_RET(request.data.auth.pass, pass);
833 if (*user == WB_AIX_ENCODED) {
837 result = winbindd_request_response(WINBINDD_PAM_AUTH, &request, &response);
839 free_response(&response);
841 logit("auth result %d for '%s'\n", result, user);
843 if (result == NSS_STATUS_SUCCESS) {
853 change a user password
855 static int wb_aix_chpass(char *user, char *oldpass, char *newpass, char **message)
857 struct winbindd_request request;
858 struct winbindd_response response;
862 if (*user == WB_AIX_ENCODED) {
863 r_user = decode_user(r_user);
870 logit("chpass '%s' old='%s' new='%s'\n", r_user, oldpass, newpass);
874 /* Send off request */
875 ZERO_STRUCT(request);
876 ZERO_STRUCT(response);
878 STRCPY_RET(request.data.chauthtok.user, r_user);
879 STRCPY_RET(request.data.chauthtok.oldpass, oldpass);
880 STRCPY_RET(request.data.chauthtok.newpass, newpass);
882 if (*user == WB_AIX_ENCODED) {
886 result = winbindd_request_response(WINBINDD_PAM_CHAUTHTOK, &request, &response);
888 free_response(&response);
890 if (result == NSS_STATUS_SUCCESS) {
900 don't do any password strength testing for now
902 static int wb_aix_passwdrestrictions(char *user, char *newpass, char *oldpass,
905 logit("passwdresrictions called for '%s'\n", user);
910 static int wb_aix_passwdexpired(char *user, char **message)
912 logit("passwdexpired '%s'\n", user);
913 /* we should check the account bits here */
919 we can't return a crypt() password
921 static char *wb_aix_getpasswd(char *user)
923 logit("getpasswd '%s'\n", user);
929 this is called to update things like the last login time. We don't
930 currently pass this onto the DC
932 static int wb_aix_putentry(char *key, char *table, char *attributes[],
933 attrval_t values[], int size)
935 logit("putentry key='%s' table='%s' attrib='%s'\n",
936 key, table, size>=1?attributes[0]:"<null>");
941 static int wb_aix_commit(char *key, char *table)
943 logit("commit key='%s' table='%s'\n");
948 static int wb_aix_getgrusers(char *group, void *result, int type, int *size)
950 logit("getgrusers group='%s'\n", group);
956 #define DECL_METHOD(x) \
957 int method_ ## x(void) \
959 logit("UNIMPLEMENTED METHOD '%s'\n", #x); \
964 #if LOG_UNIMPLEMENTED_CALLS
965 DECL_METHOD(delgroup);
966 DECL_METHOD(deluser);
967 DECL_METHOD(newgroup);
968 DECL_METHOD(newuser);
969 DECL_METHOD(putgrent);
970 DECL_METHOD(putgrusers);
971 DECL_METHOD(putpwent);
974 DECL_METHOD(getcred);
975 DECL_METHOD(setcred);
976 DECL_METHOD(deletecred);
979 int wb_aix_init(struct secmethod_table *methods)
981 ZERO_STRUCTP(methods);
983 #ifdef HAVE_STRUCT_SECMETHOD_TABLE_METHOD_VERSION
984 methods->method_version = SECMETHOD_VERSION_520;
987 methods->method_getgrgid = wb_aix_getgrgid;
988 methods->method_getgrnam = wb_aix_getgrnam;
989 methods->method_getgrset = wb_aix_getgrset;
990 methods->method_getpwnam = wb_aix_getpwnam;
991 methods->method_getpwuid = wb_aix_getpwuid;
992 methods->method_getentry = wb_aix_getentry;
993 methods->method_open = wb_aix_open;
994 methods->method_close = wb_aix_close;
995 methods->method_normalize = wb_aix_normalize;
996 methods->method_passwdexpired = wb_aix_passwdexpired;
997 methods->method_putentry = wb_aix_putentry;
998 methods->method_getpasswd = wb_aix_getpasswd;
999 methods->method_authenticate = wb_aix_authenticate;
1000 methods->method_commit = wb_aix_commit;
1001 methods->method_chpass = wb_aix_chpass;
1002 methods->method_passwdrestrictions = wb_aix_passwdrestrictions;
1003 methods->method_getgracct = wb_aix_getgracct;
1004 methods->method_getgrusers = wb_aix_getgrusers;
1005 #ifdef HAVE_STRUCT_SECMETHOD_TABLE_METHOD_ATTRLIST
1006 methods->method_attrlist = wb_aix_attrlist;
1009 #if LOG_UNIMPLEMENTED_CALLS
1010 methods->method_delgroup = method_delgroup;
1011 methods->method_deluser = method_deluser;
1012 methods->method_newgroup = method_newgroup;
1013 methods->method_newuser = method_newuser;
1014 methods->method_putgrent = method_putgrent;
1015 methods->method_putgrusers = method_putgrusers;
1016 methods->method_putpwent = method_putpwent;
1017 methods->method_lock = method_lock;
1018 methods->method_unlock = method_unlock;
1019 methods->method_getcred = method_getcred;
1020 methods->method_setcred = method_setcred;
1021 methods->method_deletecred = method_deletecred;
1024 return AUTH_SUCCESS;