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
.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
.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 ZERO_STRUCT(response
);
364 ZERO_STRUCT(request
);
366 STRCPY_RETNULL(request
.data
.username
, r_user
);
368 if (*user
== WB_AIX_ENCODED
) {
372 ret
= winbindd_request_response(WINBINDD_GETGROUPS
, &request
, &response
);
376 num_gids
= response
.data
.num_entries
;
377 gid_list
= (gid_t
*)response
.extra_data
.data
;
379 /* allocate a space large enough to contruct the string */
380 tmpbuf
= malloc(num_gids
*12);
385 for (idx
=i
=0; i
< num_gids
-1; i
++) {
386 idx
+= sprintf(tmpbuf
+idx
, "%u,", gid_list
[i
]);
388 idx
+= sprintf(tmpbuf
+idx
, "%u", gid_list
[i
]);
390 free_response(&response
);
396 /* take a uid and return a filled struct passwd */
397 static struct passwd
*wb_aix_getpwuid(uid_t uid
)
399 struct winbindd_response response
;
400 struct winbindd_request request
;
404 logit("getpwuid '%d'\n", uid
);
406 ZERO_STRUCT(response
);
407 ZERO_STRUCT(request
);
409 request
.data
.uid
= uid
;
411 ret
= winbindd_request_response(WINBINDD_GETPWUID
, &request
, &response
);
415 pwd
= fill_pwent(&response
.data
.pw
);
417 free_response(&response
);
419 logit("getpwuid gave ptr %p\n", pwd
);
425 /* take a username and return a filled struct passwd */
426 static struct passwd
*wb_aix_getpwnam(const char *name
)
428 struct winbindd_response response
;
429 struct winbindd_request request
;
433 if (*name
== WB_AIX_ENCODED
) {
434 return wb_aix_getpwuid(decode_id(name
));
437 logit("getpwnam '%s'\n", name
);
439 ZERO_STRUCT(response
);
440 ZERO_STRUCT(request
);
442 STRCPY_RETNULL(request
.data
.username
, name
);
444 ret
= winbindd_request_response(WINBINDD_GETPWNAM
, &request
, &response
);
448 pwd
= fill_pwent(&response
.data
.pw
);
450 free_response(&response
);
452 logit("getpwnam gave ptr %p\n", pwd
);
460 static int wb_aix_lsuser(char *attributes
[], attrval_t results
[], int size
)
463 struct winbindd_request request
;
464 struct winbindd_response response
;
468 if (size
!= 1 || strcmp(attributes
[0], S_USERS
) != 0) {
469 logit("invalid lsuser op\n");
474 ZERO_STRUCT(request
);
475 ZERO_STRUCT(response
);
477 ret
= winbindd_request_response(WINBINDD_LIST_USERS
, &request
, &response
);
483 len
= strlen(response
.extra_data
.data
);
487 free_response(&response
);
492 memcpy(s
, response
.extra_data
.data
, len
+1);
496 results
[0].attr_un
.au_char
= s
;
497 results
[0].attr_flag
= 0;
499 free_response(&response
);
508 static int wb_aix_lsgroup(char *attributes
[], attrval_t results
[], int size
)
511 struct winbindd_request request
;
512 struct winbindd_response response
;
516 if (size
!= 1 || strcmp(attributes
[0], S_GROUPS
) != 0) {
517 logit("invalid lsgroup op\n");
522 ZERO_STRUCT(request
);
523 ZERO_STRUCT(response
);
525 ret
= winbindd_request_response(WINBINDD_LIST_GROUPS
, &request
, &response
);
531 len
= strlen(response
.extra_data
.data
);
535 free_response(&response
);
540 memcpy(s
, response
.extra_data
.data
, len
+1);
544 results
[0].attr_un
.au_char
= s
;
545 results
[0].attr_flag
= 0;
547 free_response(&response
);
553 static attrval_t
pwd_to_group(struct passwd
*pwd
)
556 struct group
*grp
= wb_aix_getgrgid(pwd
->pw_gid
);
559 r
.attr_flag
= EINVAL
;
562 r
.attr_un
.au_char
= strdup(grp
->gr_name
);
569 static attrval_t
pwd_to_groupsids(struct passwd
*pwd
)
574 s
= wb_aix_getgrset(pwd
->pw_name
);
576 r
.attr_flag
= EINVAL
;
580 p
= malloc(strlen(s
)+2);
582 r
.attr_flag
= ENOMEM
;
590 r
.attr_un
.au_char
= p
;
595 static attrval_t
pwd_to_sid(struct passwd
*pwd
)
597 struct winbindd_request request
;
598 struct winbindd_response response
;
601 ZERO_STRUCT(request
);
602 ZERO_STRUCT(response
);
604 request
.data
.uid
= pwd
->pw_uid
;
606 if (winbindd_request_response(WINBINDD_UID_TO_SID
, &request
, &response
) !=
607 NSS_STATUS_SUCCESS
) {
608 r
.attr_flag
= ENOENT
;
611 r
.attr_un
.au_char
= strdup(response
.data
.sid
.sid
);
617 static int wb_aix_user_attrib(const char *key
, char *attributes
[],
618 attrval_t results
[], int size
)
623 pwd
= wb_aix_getpwnam(key
);
629 for (i
=0;i
<size
;i
++) {
630 results
[i
].attr_flag
= 0;
632 if (strcmp(attributes
[i
], S_ID
) == 0) {
633 results
[i
].attr_un
.au_int
= pwd
->pw_uid
;
634 } else if (strcmp(attributes
[i
], S_PWD
) == 0) {
635 results
[i
].attr_un
.au_char
= strdup(pwd
->pw_passwd
);
636 } else if (strcmp(attributes
[i
], S_HOME
) == 0) {
637 results
[i
].attr_un
.au_char
= strdup(pwd
->pw_dir
);
638 } else if (strcmp(attributes
[i
], S_SHELL
) == 0) {
639 results
[i
].attr_un
.au_char
= strdup(pwd
->pw_shell
);
640 } else if (strcmp(attributes
[i
], S_REGISTRY
) == 0) {
641 results
[i
].attr_un
.au_char
= strdup("WINBIND");
642 } else if (strcmp(attributes
[i
], S_GECOS
) == 0) {
643 results
[i
].attr_un
.au_char
= strdup(pwd
->pw_gecos
);
644 } else if (strcmp(attributes
[i
], S_PGRP
) == 0) {
645 results
[i
] = pwd_to_group(pwd
);
646 } else if (strcmp(attributes
[i
], S_GROUPS
) == 0) {
647 results
[i
] = pwd_to_groupsids(pwd
);
648 } else if (strcmp(attributes
[i
], "SID") == 0) {
649 results
[i
] = pwd_to_sid(pwd
);
651 logit("Unknown user attribute '%s'\n", attributes
[i
]);
652 results
[i
].attr_flag
= EINVAL
;
661 static int wb_aix_group_attrib(const char *key
, char *attributes
[],
662 attrval_t results
[], int size
)
667 grp
= wb_aix_getgrnam(key
);
673 for (i
=0;i
<size
;i
++) {
674 results
[i
].attr_flag
= 0;
676 if (strcmp(attributes
[i
], S_PWD
) == 0) {
677 results
[i
].attr_un
.au_char
= strdup(grp
->gr_passwd
);
678 } else if (strcmp(attributes
[i
], S_ID
) == 0) {
679 results
[i
].attr_un
.au_int
= grp
->gr_gid
;
681 logit("Unknown group attribute '%s'\n", attributes
[i
]);
682 results
[i
].attr_flag
= EINVAL
;
693 called for user/group enumerations
695 static int wb_aix_getentry(char *key
, char *table
, char *attributes
[],
696 attrval_t results
[], int size
)
698 logit("Got getentry with key='%s' table='%s' size=%d attributes[0]='%s'\n",
699 key
, table
, size
, attributes
[0]);
701 if (strcmp(key
, "ALL") == 0 &&
702 strcmp(table
, "user") == 0) {
703 return wb_aix_lsuser(attributes
, results
, size
);
706 if (strcmp(key
, "ALL") == 0 &&
707 strcmp(table
, "group") == 0) {
708 return wb_aix_lsgroup(attributes
, results
, size
);
711 if (strcmp(table
, "user") == 0) {
712 return wb_aix_user_attrib(key
, attributes
, results
, size
);
715 if (strcmp(table
, "group") == 0) {
716 return wb_aix_group_attrib(key
, attributes
, results
, size
);
719 logit("Unknown getentry operation key='%s' table='%s'\n", key
, table
);
728 called to start the backend
730 static void *wb_aix_open(const char *name
, const char *domain
, int mode
, char *options
)
732 if (strstr(options
, "debug")) {
735 logit("open name='%s' mode=%d domain='%s' options='%s'\n", name
, domain
,
740 static void wb_aix_close(void *token
)
746 #ifdef HAVE_STRUCT_SECMETHOD_TABLE_METHOD_ATTRLIST
748 return a list of additional attributes supported by the backend
750 static attrlist_t
**wb_aix_attrlist(void)
753 logit("method attrlist called\n");
754 ret
= malloc(2*sizeof(attrlist_t
*) + sizeof(attrlist_t
));
760 ret
[0] = (attrlist_t
*)(ret
+2);
762 /* just one extra attribute - the windows SID */
763 ret
[0]->al_name
= strdup("SID");
764 ret
[0]->al_flags
= AL_USERATTR
;
765 ret
[0]->al_type
= SEC_CHAR
;
774 turn a long username into a short one. Needed to cope with the 8 char
775 username limit in AIX 5.2 and below
777 static int wb_aix_normalize(char *longname
, char *shortname
)
781 logit("normalize '%s'\n", longname
);
783 /* automatically cope with AIX 5.3 with longer usernames
785 if (S_NAMELEN
> strlen(longname
)) {
786 strcpy(shortname
, longname
);
790 pwd
= wb_aix_getpwnam(longname
);
796 sprintf(shortname
, "%c%07u", WB_AIX_ENCODED
, pwd
->pw_uid
);
807 static int wb_aix_authenticate(char *user
, char *pass
,
808 int *reenter
, char **message
)
810 struct winbindd_request request
;
811 struct winbindd_response response
;
815 logit("authenticate '%s' response='%s'\n", user
, pass
);
820 /* Send off request */
821 ZERO_STRUCT(request
);
822 ZERO_STRUCT(response
);
824 if (*user
== WB_AIX_ENCODED
) {
825 r_user
= decode_user(r_user
);
827 return AUTH_NOTFOUND
;
831 STRCPY_RET(request
.data
.auth
.user
, r_user
);
832 STRCPY_RET(request
.data
.auth
.pass
, pass
);
834 if (*user
== WB_AIX_ENCODED
) {
838 result
= winbindd_request_response(WINBINDD_PAM_AUTH
, &request
, &response
);
840 free_response(&response
);
842 logit("auth result %d for '%s'\n", result
, user
);
844 if (result
== NSS_STATUS_SUCCESS
) {
854 change a user password
856 static int wb_aix_chpass(char *user
, char *oldpass
, char *newpass
, char **message
)
858 struct winbindd_request request
;
859 struct winbindd_response response
;
863 if (*user
== WB_AIX_ENCODED
) {
864 r_user
= decode_user(r_user
);
871 logit("chpass '%s' old='%s' new='%s'\n", r_user
, oldpass
, newpass
);
875 /* Send off request */
876 ZERO_STRUCT(request
);
877 ZERO_STRUCT(response
);
879 STRCPY_RET(request
.data
.chauthtok
.user
, r_user
);
880 STRCPY_RET(request
.data
.chauthtok
.oldpass
, oldpass
);
881 STRCPY_RET(request
.data
.chauthtok
.newpass
, newpass
);
883 if (*user
== WB_AIX_ENCODED
) {
887 result
= winbindd_request_response(WINBINDD_PAM_CHAUTHTOK
, &request
, &response
);
889 free_response(&response
);
891 if (result
== NSS_STATUS_SUCCESS
) {
901 don't do any password strength testing for now
903 static int wb_aix_passwdrestrictions(char *user
, char *newpass
, char *oldpass
,
906 logit("passwdresrictions called for '%s'\n", user
);
911 static int wb_aix_passwdexpired(char *user
, char **message
)
913 logit("passwdexpired '%s'\n", user
);
914 /* we should check the account bits here */
920 we can't return a crypt() password
922 static char *wb_aix_getpasswd(char *user
)
924 logit("getpasswd '%s'\n", user
);
930 this is called to update things like the last login time. We don't
931 currently pass this onto the DC
933 static int wb_aix_putentry(char *key
, char *table
, char *attributes
[],
934 attrval_t values
[], int size
)
936 logit("putentry key='%s' table='%s' attrib='%s'\n",
937 key
, table
, size
>=1?attributes
[0]:"<null>");
942 static int wb_aix_commit(char *key
, char *table
)
944 logit("commit key='%s' table='%s'\n");
949 static int wb_aix_getgrusers(char *group
, void *result
, int type
, int *size
)
951 logit("getgrusers group='%s'\n", group
);
957 #define DECL_METHOD(x) \
958 int method_ ## x(void) \
960 logit("UNIMPLEMENTED METHOD '%s'\n", #x); \
965 #if LOG_UNIMPLEMENTED_CALLS
966 DECL_METHOD(delgroup
);
967 DECL_METHOD(deluser
);
968 DECL_METHOD(newgroup
);
969 DECL_METHOD(newuser
);
970 DECL_METHOD(putgrent
);
971 DECL_METHOD(putgrusers
);
972 DECL_METHOD(putpwent
);
975 DECL_METHOD(getcred
);
976 DECL_METHOD(setcred
);
977 DECL_METHOD(deletecred
);
980 int wb_aix_init(struct secmethod_table
*methods
)
982 ZERO_STRUCTP(methods
);
984 #ifdef HAVE_STRUCT_SECMETHOD_TABLE_METHOD_VERSION
985 methods
->method_version
= SECMETHOD_VERSION_520
;
988 methods
->method_getgrgid
= wb_aix_getgrgid
;
989 methods
->method_getgrnam
= wb_aix_getgrnam
;
990 methods
->method_getgrset
= wb_aix_getgrset
;
991 methods
->method_getpwnam
= wb_aix_getpwnam
;
992 methods
->method_getpwuid
= wb_aix_getpwuid
;
993 methods
->method_getentry
= wb_aix_getentry
;
994 methods
->method_open
= wb_aix_open
;
995 methods
->method_close
= wb_aix_close
;
996 methods
->method_normalize
= wb_aix_normalize
;
997 methods
->method_passwdexpired
= wb_aix_passwdexpired
;
998 methods
->method_putentry
= wb_aix_putentry
;
999 methods
->method_getpasswd
= wb_aix_getpasswd
;
1000 methods
->method_authenticate
= wb_aix_authenticate
;
1001 methods
->method_commit
= wb_aix_commit
;
1002 methods
->method_chpass
= wb_aix_chpass
;
1003 methods
->method_passwdrestrictions
= wb_aix_passwdrestrictions
;
1004 methods
->method_getgracct
= wb_aix_getgracct
;
1005 methods
->method_getgrusers
= wb_aix_getgrusers
;
1006 #ifdef HAVE_STRUCT_SECMETHOD_TABLE_METHOD_ATTRLIST
1007 methods
->method_attrlist
= wb_aix_attrlist
;
1010 #if LOG_UNIMPLEMENTED_CALLS
1011 methods
->method_delgroup
= method_delgroup
;
1012 methods
->method_deluser
= method_deluser
;
1013 methods
->method_newgroup
= method_newgroup
;
1014 methods
->method_newuser
= method_newuser
;
1015 methods
->method_putgrent
= method_putgrent
;
1016 methods
->method_putgrusers
= method_putgrusers
;
1017 methods
->method_putpwent
= method_putpwent
;
1018 methods
->method_lock
= method_lock
;
1019 methods
->method_unlock
= method_unlock
;
1020 methods
->method_getcred
= method_getcred
;
1021 methods
->method_setcred
= method_setcred
;
1022 methods
->method_deletecred
= method_deletecred
;
1025 return AUTH_SUCCESS
;