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_request request = {
273 .wb_flags = WBFLAG_FROM_NSS,
275 struct winbindd_response response = {
281 logit("getgrgid %d\n", gid);
283 request.data.gid = gid;
285 ret = winbindd_request_response(NULL, WINBINDD_GETGRGID,
286 &request, &response);
288 logit("getgrgid ret=%d\n", ret);
292 grp = fill_grent(&response.data.gr, response.extra_data.data);
294 winbindd_free_response(&response);
299 /* take a group name and return a filled struct group */
300 static struct group *wb_aix_getgrnam(const char *name)
302 struct winbindd_request request = {
303 .wb_flags = WBFLAG_FROM_NSS,
305 struct winbindd_response response = {
311 if (*name == WB_AIX_ENCODED) {
312 return wb_aix_getgrgid(decode_id(name));
315 logit("getgrnam '%s'\n", name);
317 STRCPY_RETNULL(request.data.groupname, name);
319 ret = winbindd_request_response(NULL, WINBINDD_GETGRNAM,
320 &request, &response);
324 grp = fill_grent(&response.data.gr, response.extra_data.data);
326 winbindd_free_response(&response);
332 /* this call doesn't have to fill in the gr_mem, but we do anyway
334 static struct group *wb_aix_getgracct(void *id, int type)
337 return wb_aix_getgrnam((char *)id);
340 return wb_aix_getgrgid(*(int *)id);
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)
351 struct winbindd_request request = {
352 .wb_flags = WBFLAG_FROM_NSS,
354 struct winbindd_response response = {
364 if (*user == WB_AIX_ENCODED) {
365 r_user = decode_user(r_user);
372 logit("getgrset '%s'\n", r_user);
374 STRCPY_RETNULL(request.data.username, r_user);
376 if (*user == WB_AIX_ENCODED) {
380 ret = winbindd_request_response(NULL, WINBINDD_GETGROUPS,
381 &request, &response);
385 num_gids = response.data.num_entries;
386 gid_list = (gid_t *)response.extra_data.data;
388 /* allocate a space large enough to contruct the string */
389 tmpbuf = malloc(num_gids*12);
394 for (idx=i=0; i < num_gids-1; i++) {
395 idx += sprintf(tmpbuf+idx, "%u,", gid_list[i]);
397 idx += sprintf(tmpbuf+idx, "%u", gid_list[i]);
399 winbindd_free_response(&response);
405 /* take a uid and return a filled struct passwd */
406 static struct passwd *wb_aix_getpwuid(uid_t uid)
408 struct winbindd_request request = {
409 .wb_flags = WBFLAG_FROM_NSS,
411 struct winbindd_response response = {
417 logit("getpwuid '%d'\n", uid);
419 request.data.uid = uid;
421 ret = winbindd_request_response(NULL, WINBINDD_GETPWUID,
422 &request, &response);
426 pwd = fill_pwent(&response.data.pw);
428 winbindd_free_response(&response);
430 logit("getpwuid gave ptr %p\n", pwd);
436 /* take a username and return a filled struct passwd */
437 static struct passwd *wb_aix_getpwnam(const char *name)
439 struct winbindd_request request = {
440 .wb_flags = WBFLAG_FROM_NSS,
442 struct winbindd_response response = {
448 if (*name == WB_AIX_ENCODED) {
449 return wb_aix_getpwuid(decode_id(name));
452 logit("getpwnam '%s'\n", name);
454 STRCPY_RETNULL(request.data.username, name);
456 ret = winbindd_request_response(NULL, WINBINDD_GETPWNAM,
457 &request, &response);
461 pwd = fill_pwent(&response.data.pw);
463 winbindd_free_response(&response);
465 logit("getpwnam gave ptr %p\n", pwd);
473 static int wb_aix_lsuser(char *attributes[], attrval_t results[], int size)
476 struct winbindd_request request = {
477 .wb_flags = WBFLAG_FROM_NSS,
479 struct winbindd_response response = {
485 if (size != 1 || strcmp(attributes[0], S_USERS) != 0) {
486 logit("invalid lsuser op\n");
491 ret = winbindd_request_response(NULL, WINBINDD_LIST_USERS,
492 &request, &response);
498 len = strlen(response.extra_data.data);
502 winbindd_free_response(&response);
507 memcpy(s, response.extra_data.data, len+1);
511 results[0].attr_un.au_char = s;
512 results[0].attr_flag = 0;
514 winbindd_free_response(&response);
523 static int wb_aix_lsgroup(char *attributes[], attrval_t results[], int size)
526 struct winbindd_request request = {
527 .wb_flags = WBFLAG_FROM_NSS,
529 struct winbindd_response response = {
535 if (size != 1 || strcmp(attributes[0], S_GROUPS) != 0) {
536 logit("invalid lsgroup op\n");
541 ret = winbindd_request_response(NULL, WINBINDD_LIST_GROUPS,
542 &request, &response);
548 len = strlen(response.extra_data.data);
552 winbindd_free_response(&response);
557 memcpy(s, response.extra_data.data, len+1);
561 results[0].attr_un.au_char = s;
562 results[0].attr_flag = 0;
564 winbindd_free_response(&response);
570 static attrval_t pwd_to_group(struct passwd *pwd)
573 struct group *grp = wb_aix_getgrgid(pwd->pw_gid);
576 r.attr_flag = EINVAL;
579 r.attr_un.au_char = strdup(grp->gr_name);
586 static attrval_t pwd_to_groupsids(struct passwd *pwd)
592 if ( (s = wb_aix_getgrset(pwd->pw_name)) == NULL ) {
593 r.attr_flag = EINVAL;
598 if ( (p = malloc(mlen)) == NULL ) {
599 r.attr_flag = ENOMEM;
608 r.attr_un.au_char = p;
613 static attrval_t pwd_to_sid(struct passwd *pwd)
615 char buf[(1 /* U/G */ + 10 /* 2^32 */ + 1 /* \n */) + 1] = { 0, };
617 struct winbindd_request request;
618 struct winbindd_response response;
624 len = snprintf(buf, sizeof(buf),
626 (uint32_t)pwd->pw_uid);
627 if (len >= sizeof(buf)) {
634 request = (struct winbindd_request) {
635 .wb_flags = WBFLAG_FROM_NSS,
636 .extra_data.data = buf,
637 .extra_len = strlen(buf)+1,
639 response = (struct winbindd_response) {
643 result = winbindd_request_response(NULL, WINBINDD_XIDS_TO_SIDS,
644 &request, &response);
645 if (result == NSS_STATUS_SUCCESS) {
647 r.attr_un.au_char = strdup(response.data.sid.sid);
653 static int wb_aix_user_attrib(const char *key, char *attributes[],
654 attrval_t results[], int size)
659 pwd = wb_aix_getpwnam(key);
665 for (i=0;i<size;i++) {
666 results[i].attr_flag = 0;
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;
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);
693 logit("Unknown user attribute '%s'\n", attributes[i]);
694 results[i].attr_flag = EINVAL;
703 static int wb_aix_group_attrib(const char *key, char *attributes[],
704 attrval_t results[], int size)
709 grp = wb_aix_getgrnam(key);
715 for (i=0;i<size;i++) {
716 results[i].attr_flag = 0;
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;
723 logit("Unknown group attribute '%s'\n", attributes[i]);
724 results[i].attr_flag = EINVAL;
735 called for user/group enumerations
737 static int wb_aix_getentry(char *key, char *table, char *attributes[],
738 attrval_t results[], int size)
740 logit("Got getentry with key='%s' table='%s' size=%d attributes[0]='%s'\n",
741 key, table, size, attributes[0]);
743 if (strcmp(key, "ALL") == 0 &&
744 strcmp(table, "user") == 0) {
745 return wb_aix_lsuser(attributes, results, size);
748 if (strcmp(key, "ALL") == 0 &&
749 strcmp(table, "group") == 0) {
750 return wb_aix_lsgroup(attributes, results, size);
753 if (strcmp(table, "user") == 0) {
754 return wb_aix_user_attrib(key, attributes, results, size);
757 if (strcmp(table, "group") == 0) {
758 return wb_aix_group_attrib(key, attributes, results, size);
761 logit("Unknown getentry operation key='%s' table='%s'\n", key, table);
770 called to start the backend
772 static void *wb_aix_open(const char *name, const char *domain, int mode, char *options)
774 if (strstr(options, "debug")) {
777 logit("open name='%s' mode=%d domain='%s' options='%s'\n", name, domain,
782 static void wb_aix_close(void *token)
788 #ifdef HAVE_STRUCT_SECMETHOD_TABLE_METHOD_ATTRLIST
790 return a list of additional attributes supported by the backend
792 static attrlist_t **wb_aix_attrlist(void)
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. */
798 attrlist_t **ret = NULL;
799 attrlist_t *offset = NULL;
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},
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},
824 /* group attributes */
825 {S_ID, AL_GROUPATTR, SEC_INT}
828 logit("method attrlist called\n");
830 n = sizeof(attr_list) / sizeof(struct attr_types);
831 size = (n*sizeof(attrlist_t *));
833 if ( (ret = malloc( size )) == NULL ) {
838 /* offset to where the structures start in the buffer */
840 offset = (attrlist_t *)(ret + n);
842 /* now loop over the user_attr_list[] array and add
845 for ( i=0; i<n; i++ ) {
846 attrlist_t *a = malloc(sizeof(attrlist_t));
849 /* this is bad. Just bail */
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;
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
870 static int wb_aix_normalize(char *longname, char *shortname)
874 logit("normalize '%s'\n", longname);
876 /* automatically cope with AIX 5.3 with longer usernames
878 if (S_NAMELEN > strlen(longname)) {
879 strncpy(shortname, longname, S_NAMELEN);
880 shortname[S_NAMELEN-1] = '\0';
884 pwd = wb_aix_getpwnam(longname);
890 sprintf(shortname, "%c%07u", WB_AIX_ENCODED, pwd->pw_uid);
901 static int wb_aix_authenticate(char *user, char *pass,
902 int *reenter, char **message)
904 struct winbindd_request request = {
905 .wb_flags = WBFLAG_FROM_NSS,
907 struct winbindd_response response = {
913 logit("authenticate '%s' response='%s'\n", user, pass);
918 /* Send off request */
919 if (*user == WB_AIX_ENCODED) {
920 r_user = decode_user(r_user);
922 return AUTH_NOTFOUND;
926 STRCPY_RET(request.data.auth.user, r_user);
927 STRCPY_RET(request.data.auth.pass, pass);
929 if (*user == WB_AIX_ENCODED) {
933 result = winbindd_request_response(NULL, WINBINDD_PAM_AUTH,
934 &request, &response);
936 winbindd_free_response(&response);
938 logit("auth result %d for '%s'\n", result, user);
940 if (result == NSS_STATUS_SUCCESS) {
950 change a user password
952 static int wb_aix_chpass(char *user, char *oldpass, char *newpass, char **message)
954 struct winbindd_request request = {
955 .wb_flags = WBFLAG_FROM_NSS,
957 struct winbindd_response response = {
963 if (*user == WB_AIX_ENCODED) {
964 r_user = decode_user(r_user);
971 logit("chpass '%s' old='%s' new='%s'\n", r_user, oldpass, newpass);
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);
980 if (*user == WB_AIX_ENCODED) {
984 result = winbindd_request_response(NULL, WINBINDD_PAM_CHAUTHTOK,
985 &request, &response);
987 winbindd_free_response(&response);
989 if (result == NSS_STATUS_SUCCESS) {
999 don't do any password strength testing for now
1001 static int wb_aix_passwdrestrictions(char *user, char *newpass, char *oldpass,
1004 logit("passwdresrictions called for '%s'\n", user);
1009 static int wb_aix_passwdexpired(char *user, char **message)
1011 logit("passwdexpired '%s'\n", user);
1012 /* we should check the account bits here */
1018 we can't return a crypt() password
1020 static char *wb_aix_getpasswd(char *user)
1022 logit("getpasswd '%s'\n", user);
1028 this is called to update things like the last login time. We don't
1029 currently pass this onto the DC
1031 static int wb_aix_putentry(char *key, char *table, char *attributes[],
1032 attrval_t values[], int size)
1034 logit("putentry key='%s' table='%s' attrib='%s'\n",
1035 key, table, size>=1?attributes[0]:"<null>");
1040 static int wb_aix_commit(char *key, char *table)
1042 logit("commit key='%s' table='%s'\n");
1047 static int wb_aix_getgrusers(char *group, void *result, int type, int *size)
1049 logit("getgrusers group='%s'\n", group);
1055 #define DECL_METHOD(x) \
1056 int method_ ## x(void) \
1058 logit("UNIMPLEMENTED METHOD '%s'\n", #x); \
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);
1072 DECL_METHOD(unlock);
1073 DECL_METHOD(getcred);
1074 DECL_METHOD(setcred);
1075 DECL_METHOD(deletecred);
1078 int wb_aix_init(struct secmethod_table *methods)
1080 ZERO_STRUCTP(methods);
1082 #ifdef HAVE_STRUCT_SECMETHOD_TABLE_METHOD_VERSION
1083 methods->method_version = SECMETHOD_VERSION_520;
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;
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;
1123 return AUTH_SUCCESS;