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 char *decode_user(const char *name
)
167 static struct passwd
*wb_aix_getpwuid(uid_t uid
);
169 sscanf(name
+1, "%u", &id
);
170 pwd
= wb_aix_getpwuid(id
);
174 ret
= strdup(pwd
->pw_name
);
178 logit("decoded '%s' -> '%s'\n", name
, ret
);
185 fill a struct passwd from a winbindd_pw struct, allocating as a single block
187 static struct passwd
*fill_pwent(struct winbindd_pw
*pw
)
189 struct passwd
*result
;
191 result
= calloc(1, sizeof(struct passwd
));
197 result
->pw_uid
= pw
->pw_uid
;
198 result
->pw_gid
= pw
->pw_gid
;
199 result
->pw_name
= strdup(pw
->pw_name
);
200 result
->pw_passwd
= strdup(pw
->pw_passwd
);
201 result
->pw_gecos
= strdup(pw
->pw_gecos
);
202 result
->pw_dir
= strdup(pw
->pw_dir
);
203 result
->pw_shell
= strdup(pw
->pw_shell
);
210 fill a struct group from a winbindd_pw struct, allocating as a single block
212 static struct group
*fill_grent(struct winbindd_gr
*gr
, char *gr_mem
)
215 struct group
*result
;
218 result
= calloc(1, sizeof(struct group
));
224 result
->gr_gid
= gr
->gr_gid
;
226 result
->gr_name
= strdup(gr
->gr_name
);
227 result
->gr_passwd
= strdup(gr
->gr_passwd
);
229 /* Group membership */
230 if ((gr
->num_gr_mem
< 0) || !gr_mem
) {
234 if (gr
->num_gr_mem
== 0) {
239 result
->gr_mem
= (char **)malloc(sizeof(char *) * (gr
->num_gr_mem
+1));
240 if (!result
->gr_mem
) {
245 /* Start looking at extra data */
247 for (name
= strtok_r(gr_mem
, ",", &p
);
249 name
= strtok_r(NULL
, ",", &p
)) {
250 if (i
== gr
->num_gr_mem
) {
253 result
->gr_mem
[i
] = strdup(name
);
258 result
->gr_mem
[i
] = NULL
;
265 /* take a group id and return a filled struct group */
266 static struct group
*wb_aix_getgrgid(gid_t gid
)
268 struct winbindd_response response
;
269 struct winbindd_request request
;
273 logit("getgrgid %d\n", gid
);
275 ZERO_STRUCT(response
);
276 ZERO_STRUCT(request
);
278 request
.data
.gid
= gid
;
280 ret
= winbindd_request(WINBINDD_GETGRGID
, &request
, &response
);
282 logit("getgrgid ret=%d\n", ret
);
286 grp
= fill_grent(&response
.data
.gr
, response
.extra_data
);
288 free_response(&response
);
293 /* take a group name and return a filled struct group */
294 static struct group
*wb_aix_getgrnam(const char *name
)
296 struct winbindd_response response
;
297 struct winbindd_request request
;
301 if (*name
== WB_AIX_ENCODED
) {
302 return wb_aix_getgrgid(decode_id(name
));
305 logit("getgrnam '%s'\n", name
);
307 ZERO_STRUCT(response
);
308 ZERO_STRUCT(request
);
310 STRCPY_RETNULL(request
.data
.groupname
, name
);
312 ret
= winbindd_request(WINBINDD_GETGRNAM
, &request
, &response
);
316 grp
= fill_grent(&response
.data
.gr
, response
.extra_data
);
318 free_response(&response
);
324 /* this call doesn't have to fill in the gr_mem, but we do anyway
326 static struct group
*wb_aix_getgracct(void *id
, int type
)
329 return wb_aix_getgrnam((char *)id
);
332 return wb_aix_getgrgid(*(int *)id
);
339 /* take a username and return a string containing a comma-separated
340 list of group id numbers to which the user belongs */
341 static char *wb_aix_getgrset(char *user
)
343 struct winbindd_response response
;
344 struct winbindd_request request
;
352 if (*user
== WB_AIX_ENCODED
) {
353 r_user
= decode_user(r_user
);
360 logit("getgrset '%s'\n", r_user
);
362 STRCPY_RETNULL(request
.data
.username
, r_user
);
364 if (*user
== WB_AIX_ENCODED
) {
368 ret
= winbindd_request(WINBINDD_GETGROUPS
, &request
, &response
);
372 num_gids
= response
.data
.num_entries
;
373 gid_list
= (gid_t
*)response
.extra_data
;
375 /* allocate a space large enough to contruct the string */
376 tmpbuf
= malloc(num_gids
*12);
381 for (idx
=i
=0; i
< num_gids
-1; i
++) {
382 idx
+= sprintf(tmpbuf
+idx
, "%u,", gid_list
[i
]);
384 idx
+= sprintf(tmpbuf
+idx
, "%u", gid_list
[i
]);
386 free_response(&response
);
392 /* take a uid and return a filled struct passwd */
393 static struct passwd
*wb_aix_getpwuid(uid_t uid
)
395 struct winbindd_response response
;
396 struct winbindd_request request
;
400 logit("getpwuid '%d'\n", uid
);
402 ZERO_STRUCT(response
);
403 ZERO_STRUCT(request
);
405 request
.data
.uid
= uid
;
407 ret
= winbindd_request(WINBINDD_GETPWUID
, &request
, &response
);
411 pwd
= fill_pwent(&response
.data
.pw
);
413 free_response(&response
);
415 logit("getpwuid gave ptr %p\n", pwd
);
421 /* take a username and return a filled struct passwd */
422 static struct passwd
*wb_aix_getpwnam(const char *name
)
424 struct winbindd_response response
;
425 struct winbindd_request request
;
429 if (*name
== WB_AIX_ENCODED
) {
430 return wb_aix_getpwuid(decode_id(name
));
433 logit("getpwnam '%s'\n", name
);
435 ZERO_STRUCT(response
);
436 ZERO_STRUCT(request
);
438 STRCPY_RETNULL(request
.data
.username
, name
);
440 ret
= winbindd_request(WINBINDD_GETPWNAM
, &request
, &response
);
444 pwd
= fill_pwent(&response
.data
.pw
);
446 free_response(&response
);
448 logit("getpwnam gave ptr %p\n", pwd
);
456 static int wb_aix_lsuser(char *attributes
[], attrval_t results
[], int size
)
459 struct winbindd_request request
;
460 struct winbindd_response response
;
464 if (size
!= 1 || strcmp(attributes
[0], S_USERS
) != 0) {
465 logit("invalid lsuser op\n");
470 ZERO_STRUCT(request
);
471 ZERO_STRUCT(response
);
473 ret
= winbindd_request(WINBINDD_LIST_USERS
, &request
, &response
);
479 len
= strlen(response
.extra_data
);
483 free_response(&response
);
488 memcpy(s
, response
.extra_data
, len
+1);
492 results
[0].attr_un
.au_char
= s
;
493 results
[0].attr_flag
= 0;
495 free_response(&response
);
504 static int wb_aix_lsgroup(char *attributes
[], attrval_t results
[], int size
)
507 struct winbindd_request request
;
508 struct winbindd_response response
;
512 if (size
!= 1 || strcmp(attributes
[0], S_GROUPS
) != 0) {
513 logit("invalid lsgroup op\n");
518 ZERO_STRUCT(request
);
519 ZERO_STRUCT(response
);
521 ret
= winbindd_request(WINBINDD_LIST_GROUPS
, &request
, &response
);
527 len
= strlen(response
.extra_data
);
531 free_response(&response
);
536 memcpy(s
, response
.extra_data
, len
+1);
540 results
[0].attr_un
.au_char
= s
;
541 results
[0].attr_flag
= 0;
543 free_response(&response
);
549 static attrval_t
pwd_to_group(struct passwd
*pwd
)
552 struct group
*grp
= wb_aix_getgrgid(pwd
->pw_gid
);
555 r
.attr_flag
= EINVAL
;
558 r
.attr_un
.au_char
= strdup(grp
->gr_name
);
565 static attrval_t
pwd_to_groupsids(struct passwd
*pwd
)
570 s
= wb_aix_getgrset(pwd
->pw_name
);
572 r
.attr_flag
= EINVAL
;
576 p
= malloc(strlen(s
)+2);
578 r
.attr_flag
= ENOMEM
;
586 r
.attr_un
.au_char
= p
;
591 static attrval_t
pwd_to_sid(struct passwd
*pwd
)
593 struct winbindd_request request
;
594 struct winbindd_response response
;
597 ZERO_STRUCT(request
);
598 ZERO_STRUCT(response
);
600 request
.data
.uid
= pwd
->pw_uid
;
602 if (winbindd_request(WINBINDD_UID_TO_SID
, &request
, &response
) !=
603 NSS_STATUS_SUCCESS
) {
604 r
.attr_flag
= ENOENT
;
607 r
.attr_un
.au_char
= strdup(response
.data
.sid
.sid
);
613 static int wb_aix_user_attrib(const char *key
, char *attributes
[],
614 attrval_t results
[], int size
)
619 pwd
= wb_aix_getpwnam(key
);
625 for (i
=0;i
<size
;i
++) {
626 results
[i
].attr_flag
= 0;
628 if (strcmp(attributes
[i
], S_ID
) == 0) {
629 results
[i
].attr_un
.au_int
= pwd
->pw_uid
;
630 } else if (strcmp(attributes
[i
], S_PWD
) == 0) {
631 results
[i
].attr_un
.au_char
= strdup(pwd
->pw_passwd
);
632 } else if (strcmp(attributes
[i
], S_HOME
) == 0) {
633 results
[i
].attr_un
.au_char
= strdup(pwd
->pw_dir
);
634 } else if (strcmp(attributes
[0], S_SHELL
) == 0) {
635 results
[i
].attr_un
.au_char
= strdup(pwd
->pw_shell
);
636 } else if (strcmp(attributes
[0], S_REGISTRY
) == 0) {
637 results
[i
].attr_un
.au_char
= strdup("WINBIND");
638 } else if (strcmp(attributes
[0], S_GECOS
) == 0) {
639 results
[i
].attr_un
.au_char
= strdup(pwd
->pw_gecos
);
640 } else if (strcmp(attributes
[0], S_PGRP
) == 0) {
641 results
[i
] = pwd_to_group(pwd
);
642 } else if (strcmp(attributes
[0], S_GECOS
) == 0) {
643 results
[i
].attr_un
.au_char
= strdup(pwd
->pw_gecos
);
644 } else if (strcmp(attributes
[0], S_GROUPSIDS
) == 0) {
645 results
[i
] = pwd_to_groupsids(pwd
);
646 } else if (strcmp(attributes
[0], "SID") == 0) {
647 results
[i
] = pwd_to_sid(pwd
);
649 logit("Unknown user attribute '%s'\n", attributes
[i
]);
650 results
[i
].attr_flag
= EINVAL
;
659 static int wb_aix_group_attrib(const char *key
, char *attributes
[],
660 attrval_t results
[], int size
)
665 grp
= wb_aix_getgrnam(key
);
671 for (i
=0;i
<size
;i
++) {
672 results
[i
].attr_flag
= 0;
674 if (strcmp(attributes
[i
], S_PWD
) == 0) {
675 results
[i
].attr_un
.au_char
= strdup(grp
->gr_passwd
);
676 } else if (strcmp(attributes
[i
], S_ID
) == 0) {
677 results
[i
].attr_un
.au_int
= grp
->gr_gid
;
679 logit("Unknown group attribute '%s'\n", attributes
[i
]);
680 results
[i
].attr_flag
= EINVAL
;
691 called for user/group enumerations
693 static int wb_aix_getentry(char *key
, char *table
, char *attributes
[],
694 attrval_t results
[], int size
)
696 logit("Got getentry with key='%s' table='%s' size=%d attributes[0]='%s'\n",
697 key
, table
, size
, attributes
[0]);
699 if (strcmp(key
, "ALL") == 0 &&
700 strcmp(table
, "user") == 0) {
701 return wb_aix_lsuser(attributes
, results
, size
);
704 if (strcmp(key
, "ALL") == 0 &&
705 strcmp(table
, "group") == 0) {
706 return wb_aix_lsgroup(attributes
, results
, size
);
709 if (strcmp(table
, "user") == 0) {
710 return wb_aix_user_attrib(key
, attributes
, results
, size
);
713 if (strcmp(table
, "group") == 0) {
714 return wb_aix_group_attrib(key
, attributes
, results
, size
);
717 logit("Unknown getentry operation key='%s' table='%s'\n", key
, table
);
726 called to start the backend
728 static void *wb_aix_open(const char *name
, const char *domain
, int mode
, char *options
)
730 if (strstr(options
, "debug")) {
733 logit("open name='%s' mode=%d domain='%s' options='%s'\n", name
, domain
,
738 static void wb_aix_close(void *token
)
745 return a list of additional attributes supported by the backend
747 static attrlist_t
**wb_aix_attrlist(void)
750 logit("method attrlist called\n");
751 ret
= malloc(2*sizeof(attrlist_t
*) + sizeof(attrlist_t
));
757 ret
[0] = (attrlist_t
*)(ret
+2);
759 /* just one extra attribute - the windows SID */
760 ret
[0]->al_name
= strdup("SID");
761 ret
[0]->al_flags
= AL_USERATTR
;
762 ret
[0]->al_type
= SEC_CHAR
;
770 turn a long username into a short one. Needed to cope with the 8 char
771 username limit in AIX 5.2 and below
773 static int wb_aix_normalize(char *longname
, char *shortname
)
777 logit("normalize '%s'\n", longname
);
779 /* automatically cope with AIX 5.3 with longer usernames
781 if (S_NAMELEN
> strlen(longname
)) {
782 strcpy(shortname
, longname
);
786 pwd
= wb_aix_getpwnam(longname
);
792 sprintf(shortname
, "%c%07u", WB_AIX_ENCODED
, pwd
->pw_uid
);
803 static int wb_aix_authenticate(char *user
, char *pass
,
804 int *reenter
, char **message
)
806 struct winbindd_request request
;
807 struct winbindd_response response
;
811 logit("authenticate '%s' response='%s'\n", user
, pass
);
816 /* Send off request */
817 ZERO_STRUCT(request
);
818 ZERO_STRUCT(response
);
820 if (*user
== WB_AIX_ENCODED
) {
821 r_user
= decode_user(r_user
);
823 return AUTH_NOTFOUND
;
827 STRCPY_RET(request
.data
.auth
.user
, r_user
);
828 STRCPY_RET(request
.data
.auth
.pass
, pass
);
830 if (*user
== WB_AIX_ENCODED
) {
834 result
= winbindd_request(WINBINDD_PAM_AUTH
, &request
, &response
);
836 free_response(&response
);
838 logit("auth result %d for '%s'\n", result
, user
);
840 if (result
== NSS_STATUS_SUCCESS
) {
850 change a user password
852 static int wb_aix_chpass(char *user
, char *oldpass
, char *newpass
, char **message
)
854 struct winbindd_request request
;
855 struct winbindd_response response
;
859 if (*user
== WB_AIX_ENCODED
) {
860 r_user
= decode_user(r_user
);
867 logit("chpass '%s' old='%s' new='%s'\n", r_user
, oldpass
, newpass
);
871 /* Send off request */
872 ZERO_STRUCT(request
);
873 ZERO_STRUCT(response
);
875 STRCPY_RET(request
.data
.chauthtok
.user
, r_user
);
876 STRCPY_RET(request
.data
.chauthtok
.oldpass
, oldpass
);
877 STRCPY_RET(request
.data
.chauthtok
.newpass
, newpass
);
879 if (*user
== WB_AIX_ENCODED
) {
883 result
= winbindd_request(WINBINDD_PAM_CHAUTHTOK
, &request
, &response
);
885 free_response(&response
);
887 if (result
== NSS_STATUS_SUCCESS
) {
897 don't do any password strength testing for now
899 static int wb_aix_passwdrestrictions(char *user
, char *newpass
, char *oldpass
,
902 logit("passwdresrictions called for '%s'\n", user
);
907 static int wb_aix_passwdexpired(char *user
, char **message
)
909 logit("passwdexpired '%s'\n", user
);
910 /* we should check the account bits here */
916 we can't return a crypt() password
918 static char *wb_aix_getpasswd(char *user
)
920 logit("getpasswd '%s'\n", user
);
926 this is called to update things like the last login time. We don't
927 currently pass this onto the DC
929 static int wb_aix_putentry(char *key
, char *table
, char *attributes
[],
930 attrval_t values
[], int size
)
932 logit("putentry key='%s' table='%s' attrib='%s'\n",
933 key
, table
, size
>=1?attributes
[0]:"<null>");
938 static int wb_aix_commit(char *key
, char *table
)
940 logit("commit key='%s' table='%s'\n");
945 static int wb_aix_getgrusers(char *group
, void *result
, int type
, int *size
)
947 logit("getgrusers group='%s'\n", group
);
953 #define DECL_METHOD(x) \
954 int method_ ## x(void) \
956 logit("UNIMPLEMENTED METHOD '%s'\n", #x); \
961 #if LOG_UNIMPLEMENTED_CALLS
962 DECL_METHOD(delgroup
);
963 DECL_METHOD(deluser
);
964 DECL_METHOD(newgroup
);
965 DECL_METHOD(newuser
);
966 DECL_METHOD(putgrent
);
967 DECL_METHOD(putgrusers
);
968 DECL_METHOD(putpwent
);
971 DECL_METHOD(getcred
);
972 DECL_METHOD(setcred
);
973 DECL_METHOD(deletecred
);
976 int wb_aix_init(struct secmethod_table
*methods
)
978 ZERO_STRUCTP(methods
);
980 methods
->method_version
= SECMETHOD_VERSION_520
;
982 methods
->method_getgrgid
= wb_aix_getgrgid
;
983 methods
->method_getgrnam
= wb_aix_getgrnam
;
984 methods
->method_getgrset
= wb_aix_getgrset
;
985 methods
->method_getpwnam
= wb_aix_getpwnam
;
986 methods
->method_getpwuid
= wb_aix_getpwuid
;
987 methods
->method_getentry
= wb_aix_getentry
;
988 methods
->method_open
= wb_aix_open
;
989 methods
->method_close
= wb_aix_close
;
990 methods
->method_normalize
= wb_aix_normalize
;
991 methods
->method_passwdexpired
= wb_aix_passwdexpired
;
992 methods
->method_putentry
= wb_aix_putentry
;
993 methods
->method_getpasswd
= wb_aix_getpasswd
;
994 methods
->method_authenticate
= wb_aix_authenticate
;
995 methods
->method_commit
= wb_aix_commit
;
996 methods
->method_chpass
= wb_aix_chpass
;
997 methods
->method_passwdrestrictions
= wb_aix_passwdrestrictions
;
998 methods
->method_getgracct
= wb_aix_getgracct
;
999 methods
->method_getgrusers
= wb_aix_getgrusers
;
1000 methods
->method_attrlist
= wb_aix_attrlist
;
1002 #if LOG_UNIMPLEMENTED_CALLS
1003 methods
->method_delgroup
= method_delgroup
;
1004 methods
->method_deluser
= method_deluser
;
1005 methods
->method_newgroup
= method_newgroup
;
1006 methods
->method_newuser
= method_newuser
;
1007 methods
->method_putgrent
= method_putgrent
;
1008 methods
->method_putgrusers
= method_putgrusers
;
1009 methods
->method_putpwent
= method_putpwent
;
1010 methods
->method_lock
= method_lock
;
1011 methods
->method_unlock
= method_unlock
;
1012 methods
->method_getcred
= method_getcred
;
1013 methods
->method_setcred
= method_setcred
;
1014 methods
->method_deletecred
= method_deletecred
;
1017 return AUTH_SUCCESS
;