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 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.
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 Lesser General Public License
22 along with this program. If not, see <http://www.gnu.org/licenses/>.
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
30 Note that this module also provides authentication and password
31 changing routines, so you do not need to install the winbind PAM
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
38 Many thanks to Julianne Haugh for explaining some of the finer
39 details of this interface.
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
46 #include "winbind_client.h"
49 /* enable this to log which entry points have not been
51 #define LOG_UNIMPLEMENTED_CALLS 0
54 #define WB_AIX_ENCODED '_'
56 static int debug_enabled;
59 static void logit(const char *format, ...)
66 f = fopen("/tmp/WINBIND_DEBUG.log", "a");
69 vfprintf(f, format, ap);
75 #define HANDLE_ERRORS(ret) do { \
76 if ((ret) == NSS_STATUS_NOTFOUND) { \
79 } else if ((ret) != NSS_STATUS_SUCCESS) { \
85 #define STRCPY_RET(dest, src) \
87 if (strlen(src)+1 > sizeof(dest)) { errno = EINVAL; return -1; } \
88 strncpy(dest, src, sizeof(dest)); \
89 dest[sizeof(dest)-1] = '\0'; \
92 #define STRCPY_RETNULL(dest, src) \
94 if (strlen(src)+1 > sizeof(dest)) { errno = EINVAL; return NULL; } \
95 strncpy(dest, src, sizeof(dest)); \
96 dest[sizeof(dest)-1] = '\0'; \
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) {
242 free(result->gr_name);
243 free(result->gr_passwd);
249 /* Start looking at extra data */
251 for (name = strtok_r(gr_mem, ",", &p);
253 name = strtok_r(NULL, ",", &p)) {
254 if (i == gr->num_gr_mem) {
257 result->gr_mem[i] = strdup(name);
262 result->gr_mem[i] = NULL;
269 /* take a group id and return a filled struct group */
270 static struct group *wb_aix_getgrgid(gid_t gid)
272 struct winbindd_response response;
273 struct winbindd_request request;
277 logit("getgrgid %d\n", gid);
279 ZERO_STRUCT(response);
280 ZERO_STRUCT(request);
282 request.data.gid = gid;
284 ret = winbindd_request_response(NULL, WINBINDD_GETGRGID,
285 &request, &response);
287 logit("getgrgid ret=%d\n", ret);
291 grp = fill_grent(&response.data.gr, response.extra_data.data);
293 winbindd_free_response(&response);
298 /* take a group name and return a filled struct group */
299 static struct group *wb_aix_getgrnam(const char *name)
301 struct winbindd_response response;
302 struct winbindd_request request;
306 if (*name == WB_AIX_ENCODED) {
307 return wb_aix_getgrgid(decode_id(name));
310 logit("getgrnam '%s'\n", name);
312 ZERO_STRUCT(response);
313 ZERO_STRUCT(request);
315 STRCPY_RETNULL(request.data.groupname, name);
317 ret = winbindd_request_response(NULL, WINBINDD_GETGRNAM,
318 &request, &response);
322 grp = fill_grent(&response.data.gr, response.extra_data.data);
324 winbindd_free_response(&response);
330 /* this call doesn't have to fill in the gr_mem, but we do anyway
332 static struct group *wb_aix_getgracct(void *id, int type)
335 return wb_aix_getgrnam((char *)id);
338 return wb_aix_getgrgid(*(int *)id);
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)
349 struct winbindd_response response;
350 struct winbindd_request request;
358 if (*user == WB_AIX_ENCODED) {
359 r_user = decode_user(r_user);
366 logit("getgrset '%s'\n", r_user);
368 ZERO_STRUCT(response);
369 ZERO_STRUCT(request);
371 STRCPY_RETNULL(request.data.username, r_user);
373 if (*user == WB_AIX_ENCODED) {
377 ret = winbindd_request_response(NULL, WINBINDD_GETGROUPS,
378 &request, &response);
382 num_gids = response.data.num_entries;
383 gid_list = (gid_t *)response.extra_data.data;
385 /* allocate a space large enough to contruct the string */
386 tmpbuf = malloc(num_gids*12);
391 for (idx=i=0; i < num_gids-1; i++) {
392 idx += sprintf(tmpbuf+idx, "%u,", gid_list[i]);
394 idx += sprintf(tmpbuf+idx, "%u", gid_list[i]);
396 winbindd_free_response(&response);
402 /* take a uid and return a filled struct passwd */
403 static struct passwd *wb_aix_getpwuid(uid_t uid)
405 struct winbindd_response response;
406 struct winbindd_request request;
410 logit("getpwuid '%d'\n", uid);
412 ZERO_STRUCT(response);
413 ZERO_STRUCT(request);
415 request.data.uid = uid;
417 ret = winbindd_request_response(NULL, WINBINDD_GETPWUID,
418 &request, &response);
422 pwd = fill_pwent(&response.data.pw);
424 winbindd_free_response(&response);
426 logit("getpwuid gave ptr %p\n", pwd);
432 /* take a username and return a filled struct passwd */
433 static struct passwd *wb_aix_getpwnam(const char *name)
435 struct winbindd_response response;
436 struct winbindd_request request;
440 if (*name == WB_AIX_ENCODED) {
441 return wb_aix_getpwuid(decode_id(name));
444 logit("getpwnam '%s'\n", name);
446 ZERO_STRUCT(response);
447 ZERO_STRUCT(request);
449 STRCPY_RETNULL(request.data.username, name);
451 ret = winbindd_request_response(NULL, WINBINDD_GETPWNAM,
452 &request, &response);
456 pwd = fill_pwent(&response.data.pw);
458 winbindd_free_response(&response);
460 logit("getpwnam gave ptr %p\n", pwd);
468 static int wb_aix_lsuser(char *attributes[], attrval_t results[], int size)
471 struct winbindd_request request;
472 struct winbindd_response response;
476 if (size != 1 || strcmp(attributes[0], S_USERS) != 0) {
477 logit("invalid lsuser op\n");
482 ZERO_STRUCT(request);
483 ZERO_STRUCT(response);
485 ret = winbindd_request_response(NULL, WINBINDD_LIST_USERS,
486 &request, &response);
492 len = strlen(response.extra_data.data);
496 winbindd_free_response(&response);
501 memcpy(s, response.extra_data.data, len+1);
505 results[0].attr_un.au_char = s;
506 results[0].attr_flag = 0;
508 winbindd_free_response(&response);
517 static int wb_aix_lsgroup(char *attributes[], attrval_t results[], int size)
520 struct winbindd_request request;
521 struct winbindd_response response;
525 if (size != 1 || strcmp(attributes[0], S_GROUPS) != 0) {
526 logit("invalid lsgroup op\n");
531 ZERO_STRUCT(request);
532 ZERO_STRUCT(response);
534 ret = winbindd_request_response(NULL, WINBINDD_LIST_GROUPS,
535 &request, &response);
541 len = strlen(response.extra_data.data);
545 winbindd_free_response(&response);
550 memcpy(s, response.extra_data.data, len+1);
554 results[0].attr_un.au_char = s;
555 results[0].attr_flag = 0;
557 winbindd_free_response(&response);
563 static attrval_t pwd_to_group(struct passwd *pwd)
566 struct group *grp = wb_aix_getgrgid(pwd->pw_gid);
569 r.attr_flag = EINVAL;
572 r.attr_un.au_char = strdup(grp->gr_name);
579 static attrval_t pwd_to_groupsids(struct passwd *pwd)
585 if ( (s = wb_aix_getgrset(pwd->pw_name)) == NULL ) {
586 r.attr_flag = EINVAL;
591 if ( (p = malloc(mlen)) == NULL ) {
592 r.attr_flag = ENOMEM;
601 r.attr_un.au_char = p;
606 static attrval_t pwd_to_sid(struct passwd *pwd)
608 struct winbindd_request request;
609 struct winbindd_response response;
612 ZERO_STRUCT(request);
613 ZERO_STRUCT(response);
615 request.data.uid = pwd->pw_uid;
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.
625 if (winbindd_request_response(NULL, WINBINDD_UID_TO_SID,
626 &request, &response) !=
627 NSS_STATUS_SUCCESS) {
628 r.attr_flag = ENOENT;
631 r.attr_un.au_char = strdup(response.data.sid.sid);
634 r.attr_flag = ENOENT;
640 static int wb_aix_user_attrib(const char *key, char *attributes[],
641 attrval_t results[], int size)
646 pwd = wb_aix_getpwnam(key);
652 for (i=0;i<size;i++) {
653 results[i].attr_flag = 0;
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;
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);
680 logit("Unknown user attribute '%s'\n", attributes[i]);
681 results[i].attr_flag = EINVAL;
690 static int wb_aix_group_attrib(const char *key, char *attributes[],
691 attrval_t results[], int size)
696 grp = wb_aix_getgrnam(key);
702 for (i=0;i<size;i++) {
703 results[i].attr_flag = 0;
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;
710 logit("Unknown group attribute '%s'\n", attributes[i]);
711 results[i].attr_flag = EINVAL;
722 called for user/group enumerations
724 static int wb_aix_getentry(char *key, char *table, char *attributes[],
725 attrval_t results[], int size)
727 logit("Got getentry with key='%s' table='%s' size=%d attributes[0]='%s'\n",
728 key, table, size, attributes[0]);
730 if (strcmp(key, "ALL") == 0 &&
731 strcmp(table, "user") == 0) {
732 return wb_aix_lsuser(attributes, results, size);
735 if (strcmp(key, "ALL") == 0 &&
736 strcmp(table, "group") == 0) {
737 return wb_aix_lsgroup(attributes, results, size);
740 if (strcmp(table, "user") == 0) {
741 return wb_aix_user_attrib(key, attributes, results, size);
744 if (strcmp(table, "group") == 0) {
745 return wb_aix_group_attrib(key, attributes, results, size);
748 logit("Unknown getentry operation key='%s' table='%s'\n", key, table);
757 called to start the backend
759 static void *wb_aix_open(const char *name, const char *domain, int mode, char *options)
761 if (strstr(options, "debug")) {
764 logit("open name='%s' mode=%d domain='%s' options='%s'\n", name, domain,
769 static void wb_aix_close(void *token)
775 #ifdef HAVE_STRUCT_SECMETHOD_TABLE_METHOD_ATTRLIST
777 return a list of additional attributes supported by the backend
779 static attrlist_t **wb_aix_attrlist(void)
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. */
785 attrlist_t **ret = NULL;
786 attrlist_t *offset = NULL;
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},
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},
811 /* group attributes */
812 {S_ID, AL_GROUPATTR, SEC_INT}
815 logit("method attrlist called\n");
817 n = sizeof(attr_list) / sizeof(struct attr_types);
818 size = (n*sizeof(attrlist_t *));
820 if ( (ret = malloc( size )) == NULL ) {
825 /* offset to where the structures start in the buffer */
827 offset = (attrlist_t *)(ret + n);
829 /* now loop over the user_attr_list[] array and add
832 for ( i=0; i<n; i++ ) {
833 attrlist_t *a = malloc(sizeof(attrlist_t));
836 /* this is bad. Just bail */
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;
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
857 static int wb_aix_normalize(char *longname, char *shortname)
861 logit("normalize '%s'\n", longname);
863 /* automatically cope with AIX 5.3 with longer usernames
865 if (S_NAMELEN > strlen(longname)) {
866 strncpy(shortname, longname, S_NAMELEN);
867 shortname[S_NAMELEN-1] = '\0';
871 pwd = wb_aix_getpwnam(longname);
877 sprintf(shortname, "%c%07u", WB_AIX_ENCODED, pwd->pw_uid);
888 static int wb_aix_authenticate(char *user, char *pass,
889 int *reenter, char **message)
891 struct winbindd_request request;
892 struct winbindd_response response;
896 logit("authenticate '%s' response='%s'\n", user, pass);
901 /* Send off request */
902 ZERO_STRUCT(request);
903 ZERO_STRUCT(response);
905 if (*user == WB_AIX_ENCODED) {
906 r_user = decode_user(r_user);
908 return AUTH_NOTFOUND;
912 STRCPY_RET(request.data.auth.user, r_user);
913 STRCPY_RET(request.data.auth.pass, pass);
915 if (*user == WB_AIX_ENCODED) {
919 result = winbindd_request_response(NULL, WINBINDD_PAM_AUTH,
920 &request, &response);
922 winbindd_free_response(&response);
924 logit("auth result %d for '%s'\n", result, user);
926 if (result == NSS_STATUS_SUCCESS) {
936 change a user password
938 static int wb_aix_chpass(char *user, char *oldpass, char *newpass, char **message)
940 struct winbindd_request request;
941 struct winbindd_response response;
945 if (*user == WB_AIX_ENCODED) {
946 r_user = decode_user(r_user);
953 logit("chpass '%s' old='%s' new='%s'\n", r_user, oldpass, newpass);
957 /* Send off request */
958 ZERO_STRUCT(request);
959 ZERO_STRUCT(response);
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);
965 if (*user == WB_AIX_ENCODED) {
969 result = winbindd_request_response(NULL, WINBINDD_PAM_CHAUTHTOK,
970 &request, &response);
972 winbindd_free_response(&response);
974 if (result == NSS_STATUS_SUCCESS) {
984 don't do any password strength testing for now
986 static int wb_aix_passwdrestrictions(char *user, char *newpass, char *oldpass,
989 logit("passwdresrictions called for '%s'\n", user);
994 static int wb_aix_passwdexpired(char *user, char **message)
996 logit("passwdexpired '%s'\n", user);
997 /* we should check the account bits here */
1003 we can't return a crypt() password
1005 static char *wb_aix_getpasswd(char *user)
1007 logit("getpasswd '%s'\n", user);
1013 this is called to update things like the last login time. We don't
1014 currently pass this onto the DC
1016 static int wb_aix_putentry(char *key, char *table, char *attributes[],
1017 attrval_t values[], int size)
1019 logit("putentry key='%s' table='%s' attrib='%s'\n",
1020 key, table, size>=1?attributes[0]:"<null>");
1025 static int wb_aix_commit(char *key, char *table)
1027 logit("commit key='%s' table='%s'\n");
1032 static int wb_aix_getgrusers(char *group, void *result, int type, int *size)
1034 logit("getgrusers group='%s'\n", group);
1040 #define DECL_METHOD(x) \
1041 int method_ ## x(void) \
1043 logit("UNIMPLEMENTED METHOD '%s'\n", #x); \
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);
1057 DECL_METHOD(unlock);
1058 DECL_METHOD(getcred);
1059 DECL_METHOD(setcred);
1060 DECL_METHOD(deletecred);
1063 int wb_aix_init(struct secmethod_table *methods)
1065 ZERO_STRUCTP(methods);
1067 #ifdef HAVE_STRUCT_SECMETHOD_TABLE_METHOD_VERSION
1068 methods->method_version = SECMETHOD_VERSION_520;
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;
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;
1108 return AUTH_SUCCESS;