1/*
2   Unix SMB/CIFS implementation.
3
4   AIX loadable authentication module, providing identification and
5   authentication routines against Samba winbind/Windows NT Domain
6
7   Copyright (C) Tim Potter 2003
8   Copyright (C) Steve Roylance 2003
9   Copyright (C) Andrew Tridgell 2003-2004
10
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.
15
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.
20
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/>.
23*/
24
25/*
26
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
29
30  Note that this module also provides authentication and password
31  changing routines, so you do not need to install the winbind PAM
32  module.
33
34  see
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
37
38  Many thanks to Julianne Haugh for explaining some of the finer
39  details of this interface.
40
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
43
44*/
45
46#include "winbind_client.h"
47#include <usersec.h>
48
49/* enable this to log which entry points have not been
50  completed yet */
51#define LOG_UNIMPLEMENTED_CALLS 0
52
53
54#define WB_AIX_ENCODED '_'
55
56static int debug_enabled;
57
58
59static void logit(const char *format, ...)
60{
61	va_list ap;
62	FILE *f;
63	if (!debug_enabled) {
64		return;
65	}
66	f = fopen("/tmp/WINBIND_DEBUG.log", "a");
67	if (!f) return;
68	va_start(ap, format);
69	vfprintf(f, format, ap);
70	va_end(ap);
71	fclose(f);
72}
73
74
75#define HANDLE_ERRORS(ret) do { \
76	if ((ret) == NSS_STATUS_NOTFOUND) { \
77		errno = ENOENT; \
78		return NULL; \
79	} else if ((ret) != NSS_STATUS_SUCCESS) { \
80		errno = EIO; \
81		return NULL; \
82	} \
83} while (0)
84
85#define STRCPY_RET(dest, src) \
86do { \
87	if (strlen(src)+1 > sizeof(dest)) { errno = EINVAL; return -1; } \
88	strcpy(dest, src); \
89} while (0)
90
91#define STRCPY_RETNULL(dest, src) \
92do { \
93	if (strlen(src)+1 > sizeof(dest)) { errno = EINVAL; return NULL; } \
94	strcpy(dest, src); \
95} while (0)
96
97
98/* free a passwd structure */
99static void free_pwd(struct passwd *pwd)
100{
101	free(pwd->pw_name);
102	free(pwd->pw_passwd);
103	free(pwd->pw_gecos);
104	free(pwd->pw_dir);
105	free(pwd->pw_shell);
106	free(pwd);
107}
108
109/* free a group structure */
110static void free_grp(struct group *grp)
111{
112	int i;
113
114	free(grp->gr_name);
115	free(grp->gr_passwd);
116
117	if (!grp->gr_mem) {
118		free(grp);
119		return;
120	}
121
122	for (i=0; grp->gr_mem[i]; i++) {
123		free(grp->gr_mem[i]);
124	}
125
126	free(grp->gr_mem);
127	free(grp);
128}
129
130
131/* replace commas with nulls, and null terminate */
132static void replace_commas(char *s)
133{
134	char *p, *p0=s;
135	for (p=strchr(s, ','); p; p = strchr(p+1, ',')) {
136		*p=0;
137		p0 = p+1;
138	}
139
140	p0[strlen(p0)+1] = 0;
141}
142
143
144/* the decode_*() routines are used to cope with the fact that AIX 5.2
145   and below cannot handle user or group names longer than 8
146   characters in some interfaces. We use the normalize method to
147   provide a mapping to a username that fits, by using the form '_UID'
148   or '_GID'.
149
150   this only works if you can guarantee that the WB_AIX_ENCODED char
151   is not used as the first char of any other username
152*/
153static unsigned decode_id(const char *name)
154{
155	unsigned id;
156	sscanf(name+1, "%u", &id);
157	return id;
158}
159
160static struct passwd *wb_aix_getpwuid(uid_t uid);
161
162static char *decode_user(const char *name)
163{
164	struct passwd *pwd;
165	unsigned id;
166	char *ret;
167
168	sscanf(name+1, "%u", &id);
169	pwd = wb_aix_getpwuid(id);
170	if (!pwd) {
171		return NULL;
172	}
173	ret = strdup(pwd->pw_name);
174
175	free_pwd(pwd);
176
177	logit("decoded '%s' -> '%s'\n", name, ret);
178
179	return ret;
180}
181
182
183/*
184  fill a struct passwd from a winbindd_pw struct, allocating as a single block
185*/
186static struct passwd *fill_pwent(struct winbindd_pw *pw)
187{
188	struct passwd *result;
189
190	result = calloc(1, sizeof(struct passwd));
191	if (!result) {
192		errno = ENOMEM;
193		return NULL;
194	}
195
196	result->pw_uid = pw->pw_uid;
197	result->pw_gid = pw->pw_gid;
198	result->pw_name   = strdup(pw->pw_name);
199	result->pw_passwd = strdup(pw->pw_passwd);
200	result->pw_gecos  = strdup(pw->pw_gecos);
201	result->pw_dir    = strdup(pw->pw_dir);
202	result->pw_shell  = strdup(pw->pw_shell);
203
204	return result;
205}
206
207
208/*
209  fill a struct group from a winbindd_pw struct, allocating as a single block
210*/
211static struct group *fill_grent(struct winbindd_gr *gr, char *gr_mem)
212{
213	int i;
214	struct group *result;
215	char *p, *name;
216
217	result = calloc(1, sizeof(struct group));
218	if (!result) {
219		errno = ENOMEM;
220		return NULL;
221	}
222
223	result->gr_gid = gr->gr_gid;
224
225	result->gr_name   = strdup(gr->gr_name);
226	result->gr_passwd = strdup(gr->gr_passwd);
227
228	/* Group membership */
229	if ((gr->num_gr_mem < 0) || !gr_mem) {
230		gr->num_gr_mem = 0;
231	}
232
233	if (gr->num_gr_mem == 0) {
234		/* Group is empty */
235		return result;
236	}
237
238	result->gr_mem = (char **)malloc(sizeof(char *) * (gr->num_gr_mem+1));
239	if (!result->gr_mem) {
240		free(result->gr_name);
241		free(result->gr_passwd);
242		free(result);
243		errno = ENOMEM;
244		return NULL;
245	}
246
247	/* Start looking at extra data */
248	i=0;
249	for (name = strtok_r(gr_mem, ",", &p);
250	     name;
251	     name = strtok_r(NULL, ",", &p)) {
252		if (i == gr->num_gr_mem) {
253			break;
254		}
255		result->gr_mem[i] = strdup(name);
256		i++;
257	}
258
259	/* Terminate list */
260	result->gr_mem[i] = NULL;
261
262	return result;
263}
264
265
266
267/* take a group id and return a filled struct group */
268static struct group *wb_aix_getgrgid(gid_t gid)
269{
270	struct winbindd_response response;
271	struct winbindd_request request;
272	struct group *grp;
273	NSS_STATUS ret;
274
275	logit("getgrgid %d\n", gid);
276
277	ZERO_STRUCT(response);
278	ZERO_STRUCT(request);
279
280	request.data.gid = gid;
281
282	ret = winbindd_request_response(WINBINDD_GETGRGID, &request, &response);
283
284	logit("getgrgid ret=%d\n", ret);
285
286	HANDLE_ERRORS(ret);
287
288	grp = fill_grent(&response.data.gr, response.extra_data.data);
289
290	winbindd_free_response(&response);
291
292	return grp;
293}
294
295/* take a group name and return a filled struct group */
296static struct group *wb_aix_getgrnam(const char *name)
297{
298	struct winbindd_response response;
299	struct winbindd_request request;
300	NSS_STATUS ret;
301	struct group *grp;
302
303	if (*name == WB_AIX_ENCODED) {
304		return wb_aix_getgrgid(decode_id(name));
305	}
306
307	logit("getgrnam '%s'\n", name);
308
309	ZERO_STRUCT(response);
310	ZERO_STRUCT(request);
311
312	STRCPY_RETNULL(request.data.groupname, name);
313
314	ret = winbindd_request_response(WINBINDD_GETGRNAM, &request, &response);
315
316	HANDLE_ERRORS(ret);
317
318	grp = fill_grent(&response.data.gr, response.extra_data.data);
319
320	winbindd_free_response(&response);
321
322	return grp;
323}
324
325
326/* this call doesn't have to fill in the gr_mem, but we do anyway
327   for simplicity */
328static struct group *wb_aix_getgracct(void *id, int type)
329{
330	if (type == 1) {
331		return wb_aix_getgrnam((char *)id);
332	}
333	if (type == 0) {
334		return wb_aix_getgrgid(*(int *)id);
335	}
336	errno = EINVAL;
337	return NULL;
338}
339
340
341/* take a username and return a string containing a comma-separated
342   list of group id numbers to which the user belongs */
343static char *wb_aix_getgrset(char *user)
344{
345	struct winbindd_response response;
346	struct winbindd_request request;
347	NSS_STATUS ret;
348	int i, idx;
349	char *tmpbuf;
350	int num_gids;
351	gid_t *gid_list;
352	char *r_user = user;
353
354	if (*user == WB_AIX_ENCODED) {
355		r_user = decode_user(r_user);
356		if (!r_user) {
357			errno = ENOENT;
358			return NULL;
359		}
360	}
361
362	logit("getgrset '%s'\n", r_user);
363
364        ZERO_STRUCT(response);
365        ZERO_STRUCT(request);
366
367	STRCPY_RETNULL(request.data.username, r_user);
368
369	if (*user == WB_AIX_ENCODED) {
370		free(r_user);
371	}
372
373	ret = winbindd_request_response(WINBINDD_GETGROUPS, &request, &response);
374
375	HANDLE_ERRORS(ret);
376
377	num_gids = response.data.num_entries;
378	gid_list = (gid_t *)response.extra_data.data;
379
380	/* allocate a space large enough to contruct the string */
381	tmpbuf = malloc(num_gids*12);
382	if (!tmpbuf) {
383		return NULL;
384	}
385
386	for (idx=i=0; i < num_gids-1; i++) {
387		idx += sprintf(tmpbuf+idx, "%u,", gid_list[i]);
388	}
389	idx += sprintf(tmpbuf+idx, "%u", gid_list[i]);
390
391	winbindd_free_response(&response);
392
393	return tmpbuf;
394}
395
396
397/* take a uid and return a filled struct passwd */
398static struct passwd *wb_aix_getpwuid(uid_t uid)
399{
400	struct winbindd_response response;
401	struct winbindd_request request;
402	NSS_STATUS ret;
403	struct passwd *pwd;
404
405	logit("getpwuid '%d'\n", uid);
406
407	ZERO_STRUCT(response);
408	ZERO_STRUCT(request);
409
410	request.data.uid = uid;
411
412	ret = winbindd_request_response(WINBINDD_GETPWUID, &request, &response);
413
414	HANDLE_ERRORS(ret);
415
416	pwd = fill_pwent(&response.data.pw);
417
418	winbindd_free_response(&response);
419
420	logit("getpwuid gave ptr %p\n", pwd);
421
422	return pwd;
423}
424
425
426/* take a username and return a filled struct passwd */
427static struct passwd *wb_aix_getpwnam(const char *name)
428{
429	struct winbindd_response response;
430	struct winbindd_request request;
431	NSS_STATUS ret;
432	struct passwd *pwd;
433
434	if (*name == WB_AIX_ENCODED) {
435		return wb_aix_getpwuid(decode_id(name));
436	}
437
438	logit("getpwnam '%s'\n", name);
439
440	ZERO_STRUCT(response);
441	ZERO_STRUCT(request);
442
443	STRCPY_RETNULL(request.data.username, name);
444
445	ret = winbindd_request_response(WINBINDD_GETPWNAM, &request, &response);
446
447	HANDLE_ERRORS(ret);
448
449	pwd = fill_pwent(&response.data.pw);
450
451	winbindd_free_response(&response);
452
453	logit("getpwnam gave ptr %p\n", pwd);
454
455	return pwd;
456}
457
458/*
459  list users
460*/
461static int wb_aix_lsuser(char *attributes[], attrval_t results[], int size)
462{
463	NSS_STATUS ret;
464	struct winbindd_request request;
465	struct winbindd_response response;
466	int len;
467	char *s;
468
469	if (size != 1 || strcmp(attributes[0], S_USERS) != 0) {
470		logit("invalid lsuser op\n");
471		errno = EINVAL;
472		return -1;
473	}
474
475	ZERO_STRUCT(request);
476	ZERO_STRUCT(response);
477
478	ret = winbindd_request_response(WINBINDD_LIST_USERS, &request, &response);
479	if (ret != 0) {
480		errno = EINVAL;
481		return -1;
482	}
483
484	len = strlen(response.extra_data.data);
485
486	s = malloc(len+2);
487	if (!s) {
488		winbindd_free_response(&response);
489		errno = ENOMEM;
490		return -1;
491	}
492
493	memcpy(s, response.extra_data.data, len+1);
494
495	replace_commas(s);
496
497	results[0].attr_un.au_char = s;
498	results[0].attr_flag = 0;
499
500	winbindd_free_response(&response);
501
502	return 0;
503}
504
505
506/*
507  list groups
508*/
509static int wb_aix_lsgroup(char *attributes[], attrval_t results[], int size)
510{
511	NSS_STATUS ret;
512	struct winbindd_request request;
513	struct winbindd_response response;
514	int len;
515	char *s;
516
517	if (size != 1 || strcmp(attributes[0], S_GROUPS) != 0) {
518		logit("invalid lsgroup op\n");
519		errno = EINVAL;
520		return -1;
521	}
522
523	ZERO_STRUCT(request);
524	ZERO_STRUCT(response);
525
526	ret = winbindd_request_response(WINBINDD_LIST_GROUPS, &request, &response);
527	if (ret != 0) {
528		errno = EINVAL;
529		return -1;
530	}
531
532	len = strlen(response.extra_data.data);
533
534	s = malloc(len+2);
535	if (!s) {
536		winbindd_free_response(&response);
537		errno = ENOMEM;
538		return -1;
539	}
540
541	memcpy(s, response.extra_data.data, len+1);
542
543	replace_commas(s);
544
545	results[0].attr_un.au_char = s;
546	results[0].attr_flag = 0;
547
548	winbindd_free_response(&response);
549
550	return 0;
551}
552
553
554static attrval_t pwd_to_group(struct passwd *pwd)
555{
556	attrval_t r;
557	struct group *grp = wb_aix_getgrgid(pwd->pw_gid);
558
559	if (!grp) {
560		r.attr_flag = EINVAL;
561	} else {
562		r.attr_flag = 0;
563		r.attr_un.au_char = strdup(grp->gr_name);
564		free_grp(grp);
565	}
566
567	return r;
568}
569
570static attrval_t pwd_to_groupsids(struct passwd *pwd)
571{
572	attrval_t r;
573	char *s, *p;
574
575	if ( (s = wb_aix_getgrset(pwd->pw_name)) == NULL ) {
576		r.attr_flag = EINVAL;
577		return r;
578	}
579
580	if ( (p = malloc(strlen(s)+2)) == NULL ) {
581		r.attr_flag = ENOMEM;
582		return r;
583	}
584
585	strcpy(p, s);
586	replace_commas(p);
587	free(s);
588
589	r.attr_un.au_char = p;
590
591	return r;
592}
593
594static attrval_t pwd_to_sid(struct passwd *pwd)
595{
596	struct winbindd_request request;
597	struct winbindd_response response;
598	attrval_t r;
599
600	ZERO_STRUCT(request);
601	ZERO_STRUCT(response);
602
603	request.data.uid = pwd->pw_uid;
604
605	if (winbindd_request_response(WINBINDD_UID_TO_SID, &request, &response) !=
606	    NSS_STATUS_SUCCESS) {
607		r.attr_flag = ENOENT;
608	} else {
609		r.attr_flag = 0;
610		r.attr_un.au_char = strdup(response.data.sid.sid);
611	}
612
613	return r;
614}
615
616static int wb_aix_user_attrib(const char *key, char *attributes[],
617			      attrval_t results[], int size)
618{
619	struct passwd *pwd;
620	int i;
621
622	pwd = wb_aix_getpwnam(key);
623	if (!pwd) {
624		errno = ENOENT;
625		return -1;
626	}
627
628	for (i=0;i<size;i++) {
629		results[i].attr_flag = 0;
630
631		if (strcmp(attributes[i], S_ID) == 0) {
632			results[i].attr_un.au_int = pwd->pw_uid;
633#ifdef _AIXVERSION_530
634		} else if (strcmp(attributes[i], S_PGID) == 0) {
635			results[i].attr_un.au_int = pwd->pw_gid;
636#endif
637		} else if (strcmp(attributes[i], S_PWD) == 0) {
638			results[i].attr_un.au_char = strdup(pwd->pw_passwd);
639		} else if (strcmp(attributes[i], S_HOME) == 0) {
640			results[i].attr_un.au_char = strdup(pwd->pw_dir);
641		} else if (strcmp(attributes[i], S_SHELL) == 0) {
642			results[i].attr_un.au_char = strdup(pwd->pw_shell);
643		} else if (strcmp(attributes[i], S_REGISTRY) == 0) {
644			results[i].attr_un.au_char = strdup("WINBIND");
645		} else if (strcmp(attributes[i], S_GECOS) == 0) {
646			results[i].attr_un.au_char = strdup(pwd->pw_gecos);
647		} else if (strcmp(attributes[i], S_PGRP) == 0) {
648			results[i] = pwd_to_group(pwd);
649		} else if (strcmp(attributes[i], S_GROUPS) == 0) {
650			results[i] = pwd_to_groupsids(pwd);
651		} else if (strcmp(attributes[i], "SID") == 0) {
652			results[i] = pwd_to_sid(pwd);
653		} else {
654			logit("Unknown user attribute '%s'\n", attributes[i]);
655			results[i].attr_flag = EINVAL;
656		}
657	}
658
659	free_pwd(pwd);
660
661	return 0;
662}
663
664static int wb_aix_group_attrib(const char *key, char *attributes[],
665			       attrval_t results[], int size)
666{
667	struct group *grp;
668	int i;
669
670	grp = wb_aix_getgrnam(key);
671	if (!grp) {
672		errno = ENOENT;
673		return -1;
674	}
675
676	for (i=0;i<size;i++) {
677		results[i].attr_flag = 0;
678
679		if (strcmp(attributes[i], S_PWD) == 0) {
680			results[i].attr_un.au_char = strdup(grp->gr_passwd);
681		} else if (strcmp(attributes[i], S_ID) == 0) {
682			results[i].attr_un.au_int = grp->gr_gid;
683		} else {
684			logit("Unknown group attribute '%s'\n", attributes[i]);
685			results[i].attr_flag = EINVAL;
686		}
687	}
688
689	free_grp(grp);
690
691	return 0;
692}
693
694
695/*
696  called for user/group enumerations
697*/
698static int wb_aix_getentry(char *key, char *table, char *attributes[],
699			   attrval_t results[], int size)
700{
701	logit("Got getentry with key='%s' table='%s' size=%d attributes[0]='%s'\n",
702	      key, table, size, attributes[0]);
703
704	if (strcmp(key, "ALL") == 0 &&
705	    strcmp(table, "user") == 0) {
706		return wb_aix_lsuser(attributes, results, size);
707	}
708
709	if (strcmp(key, "ALL") == 0 &&
710	    strcmp(table, "group") == 0) {
711		return wb_aix_lsgroup(attributes, results, size);
712	}
713
714	if (strcmp(table, "user") == 0) {
715		return wb_aix_user_attrib(key, attributes, results, size);
716	}
717
718	if (strcmp(table, "group") == 0) {
719		return wb_aix_group_attrib(key, attributes, results, size);
720	}
721
722	logit("Unknown getentry operation key='%s' table='%s'\n", key, table);
723
724	errno = ENOSYS;
725	return -1;
726}
727
728
729
730/*
731  called to start the backend
732*/
733static void *wb_aix_open(const char *name, const char *domain, int mode, char *options)
734{
735	if (strstr(options, "debug")) {
736		debug_enabled = 1;
737	}
738	logit("open name='%s' mode=%d domain='%s' options='%s'\n", name, domain,
739	      mode, options);
740	return NULL;
741}
742
743static void wb_aix_close(void *token)
744{
745	logit("close\n");
746	return;
747}
748
749#ifdef HAVE_STRUCT_SECMETHOD_TABLE_METHOD_ATTRLIST
750/*
751   return a list of additional attributes supported by the backend
752*/
753static attrlist_t **wb_aix_attrlist(void)
754{
755	/* pretty confusing but we are allocating the array of pointers
756	   and the structures we'll be pointing to all at once.  So
757	   you need N+1 pointers and N structures. */
758
759	attrlist_t **ret = NULL;
760	attrlist_t *offset = NULL;
761	int i;
762	int n;
763	size_t size;
764
765	struct attr_types {
766		const char *name;
767		int flags;
768		int type;
769	} attr_list[] = {
770		/* user attributes */
771		{S_ID, 		AL_USERATTR, 	SEC_INT},
772		{S_PGRP, 	AL_USERATTR,	SEC_CHAR},
773		{S_HOME, 	AL_USERATTR, 	SEC_CHAR},
774		{S_SHELL, 	AL_USERATTR,	SEC_CHAR},
775#ifdef _AIXVERSION_530
776		{S_PGID, 	AL_USERATTR,	SEC_INT},
777#endif
778		{S_GECOS, 	AL_USERATTR,	SEC_CHAR},
779		{S_SHELL, 	AL_USERATTR,	SEC_CHAR},
780		{S_PGRP, 	AL_USERATTR,	SEC_CHAR},
781		{S_GROUPS, 	AL_USERATTR, 	SEC_LIST},
782		{"SID", 	AL_USERATTR,	SEC_CHAR},
783
784		/* group attributes */
785		{S_ID, 		AL_GROUPATTR,	SEC_INT}
786	};
787
788	logit("method attrlist called\n");
789
790	n = sizeof(attr_list) / sizeof(struct attr_types);
791	size = (n*sizeof(attrlist_t *));
792
793	if ( (ret = malloc( size )) == NULL ) {
794		errno = ENOMEM;
795		return NULL;
796	}
797
798	/* offset to where the structures start in the buffer */
799
800	offset = (attrlist_t *)(ret + n);
801
802	/* now loop over the user_attr_list[] array and add
803	   all the members */
804
805	for ( i=0; i<n; i++ ) {
806		attrlist_t *a = malloc(sizeof(attrlist_t));
807
808		if ( !a ) {
809			/* this is bad.  Just bail */
810			return NULL;
811		}
812
813		a->al_name  = strdup(attr_list[i].name);
814		a->al_flags = attr_list[i].flags;
815		a->al_type  = attr_list[i].type;
816
817		ret[i] = a;
818	}
819	ret[n] = NULL;
820
821	return ret;
822}
823#endif
824
825
826/*
827  turn a long username into a short one. Needed to cope with the 8 char
828  username limit in AIX 5.2 and below
829*/
830static int wb_aix_normalize(char *longname, char *shortname)
831{
832	struct passwd *pwd;
833
834	logit("normalize '%s'\n", longname);
835
836	/* automatically cope with AIX 5.3 with longer usernames
837	   when it comes out */
838	if (S_NAMELEN > strlen(longname)) {
839		strcpy(shortname, longname);
840		return 1;
841	}
842
843	pwd = wb_aix_getpwnam(longname);
844	if (!pwd) {
845		errno = ENOENT;
846		return 0;
847	}
848
849	sprintf(shortname, "%c%07u", WB_AIX_ENCODED, pwd->pw_uid);
850
851	free_pwd(pwd);
852
853	return 1;
854}
855
856
857/*
858  authenticate a user
859 */
860static int wb_aix_authenticate(char *user, char *pass,
861			       int *reenter, char **message)
862{
863	struct winbindd_request request;
864	struct winbindd_response response;
865        NSS_STATUS result;
866	char *r_user = user;
867
868	logit("authenticate '%s' response='%s'\n", user, pass);
869
870	*reenter = 0;
871	*message = NULL;
872
873	/* Send off request */
874	ZERO_STRUCT(request);
875	ZERO_STRUCT(response);
876
877	if (*user == WB_AIX_ENCODED) {
878		r_user = decode_user(r_user);
879		if (!r_user) {
880			return AUTH_NOTFOUND;
881		}
882	}
883
884	STRCPY_RET(request.data.auth.user, r_user);
885	STRCPY_RET(request.data.auth.pass, pass);
886
887	if (*user == WB_AIX_ENCODED) {
888		free(r_user);
889	}
890
891	result = winbindd_request_response(WINBINDD_PAM_AUTH, &request, &response);
892
893	winbindd_free_response(&response);
894
895	logit("auth result %d for '%s'\n", result, user);
896
897	if (result == NSS_STATUS_SUCCESS) {
898		errno = 0;
899		return AUTH_SUCCESS;
900	}
901
902	return AUTH_FAILURE;
903}
904
905
906/*
907  change a user password
908*/
909static int wb_aix_chpass(char *user, char *oldpass, char *newpass, char **message)
910{
911	struct winbindd_request request;
912	struct winbindd_response response;
913        NSS_STATUS result;
914	char *r_user = user;
915
916	if (*user == WB_AIX_ENCODED) {
917		r_user = decode_user(r_user);
918		if (!r_user) {
919			errno = ENOENT;
920			return -1;
921		}
922	}
923
924	logit("chpass '%s' old='%s' new='%s'\n", r_user, oldpass, newpass);
925
926	*message = NULL;
927
928	/* Send off request */
929	ZERO_STRUCT(request);
930	ZERO_STRUCT(response);
931
932	STRCPY_RET(request.data.chauthtok.user, r_user);
933	STRCPY_RET(request.data.chauthtok.oldpass, oldpass);
934	STRCPY_RET(request.data.chauthtok.newpass, newpass);
935
936	if (*user == WB_AIX_ENCODED) {
937		free(r_user);
938	}
939
940	result = winbindd_request_response(WINBINDD_PAM_CHAUTHTOK, &request, &response);
941
942	winbindd_free_response(&response);
943
944	if (result == NSS_STATUS_SUCCESS) {
945		errno = 0;
946		return 0;
947	}
948
949	errno = EINVAL;
950	return -1;
951}
952
953/*
954  don't do any password strength testing for now
955*/
956static int wb_aix_passwdrestrictions(char *user, char *newpass, char *oldpass,
957				     char **message)
958{
959	logit("passwdresrictions called for '%s'\n", user);
960	return 0;
961}
962
963
964static int wb_aix_passwdexpired(char *user, char **message)
965{
966	logit("passwdexpired '%s'\n", user);
967	/* we should check the account bits here */
968	return 0;
969}
970
971
972/*
973  we can't return a crypt() password
974*/
975static char *wb_aix_getpasswd(char *user)
976{
977	logit("getpasswd '%s'\n", user);
978	errno = ENOSYS;
979	return NULL;
980}
981
982/*
983  this is called to update things like the last login time. We don't
984  currently pass this onto the DC
985*/
986static int wb_aix_putentry(char *key, char *table, char *attributes[],
987			   attrval_t values[], int size)
988{
989	logit("putentry key='%s' table='%s' attrib='%s'\n",
990	      key, table, size>=1?attributes[0]:"<null>");
991	errno = ENOSYS;
992	return -1;
993}
994
995static int wb_aix_commit(char *key, char *table)
996{
997	logit("commit key='%s' table='%s'\n");
998	errno = ENOSYS;
999	return -1;
1000}
1001
1002static int wb_aix_getgrusers(char *group, void *result, int type, int *size)
1003{
1004	logit("getgrusers group='%s'\n", group);
1005	errno = ENOSYS;
1006	return -1;
1007}
1008
1009
1010#define DECL_METHOD(x) \
1011int method_ ## x(void) \
1012{ \
1013	logit("UNIMPLEMENTED METHOD '%s'\n", #x); \
1014	errno = EINVAL; \
1015	return -1; \
1016}
1017
1018#if LOG_UNIMPLEMENTED_CALLS
1019DECL_METHOD(delgroup);
1020DECL_METHOD(deluser);
1021DECL_METHOD(newgroup);
1022DECL_METHOD(newuser);
1023DECL_METHOD(putgrent);
1024DECL_METHOD(putgrusers);
1025DECL_METHOD(putpwent);
1026DECL_METHOD(lock);
1027DECL_METHOD(unlock);
1028DECL_METHOD(getcred);
1029DECL_METHOD(setcred);
1030DECL_METHOD(deletecred);
1031#endif
1032
1033int wb_aix_init(struct secmethod_table *methods)
1034{
1035	ZERO_STRUCTP(methods);
1036
1037#ifdef HAVE_STRUCT_SECMETHOD_TABLE_METHOD_VERSION
1038	methods->method_version = SECMETHOD_VERSION_520;
1039#endif
1040
1041	methods->method_getgrgid           = wb_aix_getgrgid;
1042	methods->method_getgrnam           = wb_aix_getgrnam;
1043	methods->method_getgrset           = wb_aix_getgrset;
1044	methods->method_getpwnam           = wb_aix_getpwnam;
1045	methods->method_getpwuid           = wb_aix_getpwuid;
1046	methods->method_getentry           = wb_aix_getentry;
1047	methods->method_open               = wb_aix_open;
1048	methods->method_close              = wb_aix_close;
1049	methods->method_normalize          = wb_aix_normalize;
1050	methods->method_passwdexpired      = wb_aix_passwdexpired;
1051	methods->method_putentry           = wb_aix_putentry;
1052	methods->method_getpasswd          = wb_aix_getpasswd;
1053	methods->method_authenticate       = wb_aix_authenticate;
1054	methods->method_commit             = wb_aix_commit;
1055	methods->method_chpass             = wb_aix_chpass;
1056	methods->method_passwdrestrictions = wb_aix_passwdrestrictions;
1057	methods->method_getgracct          = wb_aix_getgracct;
1058	methods->method_getgrusers         = wb_aix_getgrusers;
1059#ifdef HAVE_STRUCT_SECMETHOD_TABLE_METHOD_ATTRLIST
1060	methods->method_attrlist           = wb_aix_attrlist;
1061#endif
1062
1063#if LOG_UNIMPLEMENTED_CALLS
1064	methods->method_delgroup      = method_delgroup;
1065	methods->method_deluser       = method_deluser;
1066	methods->method_newgroup      = method_newgroup;
1067	methods->method_newuser       = method_newuser;
1068	methods->method_putgrent      = method_putgrent;
1069	methods->method_putgrusers    = method_putgrusers;
1070	methods->method_putpwent      = method_putpwent;
1071	methods->method_lock          = method_lock;
1072	methods->method_unlock        = method_unlock;
1073	methods->method_getcred       = method_getcred;
1074	methods->method_setcred       = method_setcred;
1075	methods->method_deletecred    = method_deletecred;
1076#endif
1077
1078	return AUTH_SUCCESS;
1079}
1080