1106121Sdes/*
2106121Sdes *
3106121Sdes * Copyright (c) 2001 Gert Doering.  All rights reserved.
4181111Sdes * Copyright (c) 2003,2004,2005,2006 Darren Tucker.  All rights reserved.
5106121Sdes *
6106121Sdes * Redistribution and use in source and binary forms, with or without
7106121Sdes * modification, are permitted provided that the following conditions
8106121Sdes * are met:
9106121Sdes * 1. Redistributions of source code must retain the above copyright
10106121Sdes *    notice, this list of conditions and the following disclaimer.
11106121Sdes * 2. Redistributions in binary form must reproduce the above copyright
12106121Sdes *    notice, this list of conditions and the following disclaimer in the
13106121Sdes *    documentation and/or other materials provided with the distribution.
14106121Sdes *
15106121Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16106121Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17106121Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18106121Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19106121Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20106121Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21106121Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22106121Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23106121Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24106121Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25106121Sdes *
26106121Sdes */
2798937Sdes#include "includes.h"
28162852Sdes
29162852Sdes#include "xmalloc.h"
30162852Sdes#include "buffer.h"
31162852Sdes#include "key.h"
32162852Sdes#include "hostfile.h"
33126274Sdes#include "auth.h"
34124208Sdes#include "ssh.h"
35124208Sdes#include "log.h"
3698937Sdes
3798937Sdes#ifdef _AIX
3898937Sdes
39162852Sdes#include <errno.h>
40162852Sdes#if defined(HAVE_NETDB_H)
41162852Sdes# include <netdb.h>
42162852Sdes#endif
4398937Sdes#include <uinfo.h>
44162852Sdes#include <stdarg.h>
45162852Sdes#include <string.h>
46162852Sdes#include <unistd.h>
47146998Sdes#include <sys/socket.h>
48162852Sdes
49162852Sdes#ifdef WITH_AIXAUTHENTICATE
50162852Sdes# include <login.h>
51162852Sdes# include <userpw.h>
52162852Sdes# if defined(HAVE_SYS_AUDIT_H) && defined(AIX_LOGINFAILED_4ARG)
53162852Sdes#  include <sys/audit.h>
54162852Sdes# endif
55162852Sdes# include <usersec.h>
56162852Sdes#endif
57162852Sdes
58124208Sdes#include "port-aix.h"
5998937Sdes
60197679Sdesstatic char *lastlogin_msg = NULL;
61197679Sdes
62126274Sdes# ifdef HAVE_SETAUTHDB
63126274Sdesstatic char old_registry[REGISTRY_SIZE] = "";
64126274Sdes# endif
65126274Sdes
6698937Sdes/*
67149749Sdes * AIX has a "usrinfo" area where logname and other stuff is stored -
68106121Sdes * a few applications actually use this and die if it's not set
69106121Sdes *
70106121Sdes * NOTE: TTY= should be set, but since no one uses it and it's hard to
71106121Sdes * acquire due to privsep code.  We will just drop support.
7298937Sdes */
7398937Sdesvoid
74106121Sdesaix_usrinfo(struct passwd *pw)
7598937Sdes{
7698937Sdes	u_int i;
77124208Sdes	size_t len;
78106121Sdes	char *cp;
7998937Sdes
80124208Sdes	len = sizeof("LOGNAME= NAME= ") + (2 * strlen(pw->pw_name));
81124208Sdes	cp = xmalloc(len);
82124208Sdes
83149749Sdes	i = snprintf(cp, len, "LOGNAME=%s%cNAME=%s%c", pw->pw_name, '\0',
84124208Sdes	    pw->pw_name, '\0');
8598937Sdes	if (usrinfo(SETUINFO, cp, i) == -1)
8698937Sdes		fatal("Couldn't set usrinfo: %s", strerror(errno));
8798937Sdes	debug3("AIX/UsrInfo: set len %d", i);
88124208Sdes
89255767Sdes	free(cp);
9098937Sdes}
9198937Sdes
92126274Sdes# ifdef WITH_AIXAUTHENTICATE
93124208Sdes/*
94124208Sdes * Remove embedded newlines in string (if any).
95124208Sdes * Used before logging messages returned by AIX authentication functions
96124208Sdes * so the message is logged on one line.
97124208Sdes */
98124208Sdesvoid
99124208Sdesaix_remove_embedded_newlines(char *p)
100124208Sdes{
101124208Sdes	if (p == NULL)
102124208Sdes		return;
103124208Sdes
104124208Sdes	for (; *p; p++) {
105124208Sdes		if (*p == '\n')
106124208Sdes			*p = ' ';
107124208Sdes	}
108124208Sdes	/* Remove trailing whitespace */
109124208Sdes	if (*--p == ' ')
110124208Sdes		*p = '\0';
111124208Sdes}
112126274Sdes
113126274Sdes/*
114146998Sdes * Test specifically for the case where SYSTEM == NONE and AUTH1 contains
115146998Sdes * anything other than NONE or SYSTEM, which indicates that the admin has
116146998Sdes * configured the account for purely AUTH1-type authentication.
117146998Sdes *
118146998Sdes * Since authenticate() doesn't check AUTH1, and sshd can't sanely support
119146998Sdes * AUTH1 itself, in such a case authenticate() will allow access without
120146998Sdes * authentation, which is almost certainly not what the admin intends.
121146998Sdes *
122146998Sdes * (The native tools, eg login, will process the AUTH1 list in addition to
123146998Sdes * the SYSTEM list by using ckuserID(), however ckuserID() and AUTH1 methods
124146998Sdes * have been deprecated since AIX 4.2.x and would be very difficult for sshd
125146998Sdes * to support.
126146998Sdes *
127146998Sdes * Returns 0 if an unsupportable combination is found, 1 otherwise.
128146998Sdes */
129146998Sdesstatic int
130146998Sdesaix_valid_authentications(const char *user)
131146998Sdes{
132146998Sdes	char *auth1, *sys, *p;
133146998Sdes	int valid = 1;
134146998Sdes
135146998Sdes	if (getuserattr((char *)user, S_AUTHSYSTEM, &sys, SEC_CHAR) != 0) {
136146998Sdes		logit("Can't retrieve attribute SYSTEM for %s: %.100s",
137146998Sdes		    user, strerror(errno));
138146998Sdes		return 0;
139146998Sdes	}
140146998Sdes
141146998Sdes	debug3("AIX SYSTEM attribute %s", sys);
142146998Sdes	if (strcmp(sys, "NONE") != 0)
143146998Sdes		return 1;	/* not "NONE", so is OK */
144146998Sdes
145146998Sdes	if (getuserattr((char *)user, S_AUTH1, &auth1, SEC_LIST) != 0) {
146146998Sdes		logit("Can't retrieve attribute auth1 for %s: %.100s",
147146998Sdes		    user, strerror(errno));
148146998Sdes		return 0;
149146998Sdes	}
150146998Sdes
151146998Sdes	p = auth1;
152146998Sdes	/* A SEC_LIST is concatenated strings, ending with two NULs. */
153146998Sdes	while (p[0] != '\0' && p[1] != '\0') {
154146998Sdes		debug3("AIX auth1 attribute list member %s", p);
155146998Sdes		if (strcmp(p, "NONE") != 0 && strcmp(p, "SYSTEM")) {
156146998Sdes			logit("Account %s has unsupported auth1 value '%s'",
157146998Sdes			    user, p);
158146998Sdes			valid = 0;
159146998Sdes		}
160146998Sdes		p += strlen(p) + 1;
161146998Sdes	}
162146998Sdes
163146998Sdes	return (valid);
164146998Sdes}
165146998Sdes
166146998Sdes/*
167126274Sdes * Do authentication via AIX's authenticate routine.  We loop until the
168126274Sdes * reenter parameter is 0, but normally authenticate is called only once.
169126274Sdes *
170126274Sdes * Note: this function returns 1 on success, whereas AIX's authenticate()
171126274Sdes * returns 0.
172126274Sdes */
173126274Sdesint
174147001Sdessys_auth_passwd(Authctxt *ctxt, const char *password)
175126274Sdes{
176149749Sdes	char *authmsg = NULL, *msg = NULL, *name = ctxt->pw->pw_name;
177126274Sdes	int authsuccess = 0, expired, reenter, result;
178126274Sdes
179126274Sdes	do {
180126274Sdes		result = authenticate((char *)name, (char *)password, &reenter,
181126274Sdes		    &authmsg);
182323134Sdes		aix_remove_embedded_newlines(authmsg);
183149749Sdes		debug3("AIX/authenticate result %d, authmsg %.100s", result,
184126274Sdes		    authmsg);
185126274Sdes	} while (reenter);
186126274Sdes
187146998Sdes	if (!aix_valid_authentications(name))
188146998Sdes		result = -1;
189146998Sdes
190126274Sdes	if (result == 0) {
191126274Sdes		authsuccess = 1;
192126274Sdes
193149749Sdes		/*
194126274Sdes		 * Record successful login.  We don't have a pty yet, so just
195126274Sdes		 * label the line as "ssh"
196126274Sdes		 */
197126274Sdes		aix_setauthdb(name);
198126274Sdes
199126274Sdes		/*
200126274Sdes		 * Check if the user's password is expired.
201126274Sdes		 */
202137015Sdes		expired = passwdexpired(name, &msg);
203137015Sdes		if (msg && *msg) {
204147001Sdes			buffer_append(ctxt->loginmsg, msg, strlen(msg));
205137015Sdes			aix_remove_embedded_newlines(msg);
206137015Sdes		}
207137015Sdes		debug3("AIX/passwdexpired returned %d msg %.100s", expired, msg);
208126274Sdes
209126274Sdes		switch (expired) {
210126274Sdes		case 0: /* password not expired */
211126274Sdes			break;
212126274Sdes		case 1: /* expired, password change required */
213126274Sdes			ctxt->force_pwchange = 1;
214126274Sdes			break;
215126274Sdes		default: /* user can't change(2) or other error (-1) */
216126274Sdes			logit("Password can't be changed for user %s: %.100s",
217126274Sdes			    name, msg);
218255767Sdes			free(msg);
219126274Sdes			authsuccess = 0;
220126274Sdes		}
221126274Sdes
222126274Sdes		aix_restoreauthdb();
223126274Sdes	}
224126274Sdes
225255767Sdes	free(authmsg);
226126274Sdes
227126274Sdes	return authsuccess;
228126274Sdes}
229137015Sdes
230137015Sdes/*
231137015Sdes * Check if specified account is permitted to log in.
232137015Sdes * Returns 1 if login is allowed, 0 if not allowed.
233137015Sdes */
234137015Sdesint
235146998Sdessys_auth_allowed_user(struct passwd *pw, Buffer *loginmsg)
236137015Sdes{
237137015Sdes	char *msg = NULL;
238137015Sdes	int result, permitted = 0;
239137015Sdes	struct stat st;
240137015Sdes
241137015Sdes	/*
242137015Sdes	 * Don't perform checks for root account (PermitRootLogin controls
243181111Sdes	 * logins via ssh) or if running as non-root user (since
244137015Sdes	 * loginrestrictions will always fail due to insufficient privilege).
245137015Sdes	 */
246137015Sdes	if (pw->pw_uid == 0 || geteuid() != 0) {
247137015Sdes		debug3("%s: not checking", __func__);
248137015Sdes		return 1;
249137015Sdes	}
250137015Sdes
251137015Sdes	result = loginrestrictions(pw->pw_name, S_RLOGIN, NULL, &msg);
252137015Sdes	if (result == 0)
253137015Sdes		permitted = 1;
254137015Sdes	/*
255137015Sdes	 * If restricted because /etc/nologin exists, the login will be denied
256137015Sdes	 * in session.c after the nologin message is sent, so allow for now
257137015Sdes	 * and do not append the returned message.
258137015Sdes	 */
259137015Sdes	if (result == -1 && errno == EPERM && stat(_PATH_NOLOGIN, &st) == 0)
260137015Sdes		permitted = 1;
261137015Sdes	else if (msg != NULL)
262146998Sdes		buffer_append(loginmsg, msg, strlen(msg));
263137015Sdes	if (msg == NULL)
264137015Sdes		msg = xstrdup("(none)");
265137015Sdes	aix_remove_embedded_newlines(msg);
266137015Sdes	debug3("AIX/loginrestrictions returned %d msg %.100s", result, msg);
267137015Sdes
268137015Sdes	if (!permitted)
269137015Sdes		logit("Login restricted for %s: %.100s", pw->pw_name, msg);
270255767Sdes	free(msg);
271137015Sdes	return permitted;
272137015Sdes}
273137015Sdes
274137015Sdesint
275146998Sdessys_auth_record_login(const char *user, const char *host, const char *ttynm,
276146998Sdes    Buffer *loginmsg)
277137015Sdes{
278149749Sdes	char *msg = NULL;
279137015Sdes	int success = 0;
280137015Sdes
281137015Sdes	aix_setauthdb(user);
282146998Sdes	if (loginsuccess((char *)user, (char *)host, (char *)ttynm, &msg) == 0) {
283137015Sdes		success = 1;
284197679Sdes		if (msg != NULL) {
285146998Sdes			debug("AIX/loginsuccess: msg %s", msg);
286197679Sdes			if (lastlogin_msg == NULL)
287197679Sdes				lastlogin_msg = msg;
288137015Sdes		}
289137015Sdes	}
290137015Sdes	aix_restoreauthdb();
291137015Sdes	return (success);
292137015Sdes}
293137015Sdes
294197679Sdeschar *
295197679Sdessys_auth_get_lastlogin_msg(const char *user, uid_t uid)
296197679Sdes{
297197679Sdes	char *msg = lastlogin_msg;
298197679Sdes
299197679Sdes	lastlogin_msg = NULL;
300197679Sdes	return msg;
301197679Sdes}
302197679Sdes
303126274Sdes#  ifdef CUSTOM_FAILED_LOGIN
304124208Sdes/*
305124208Sdes * record_failed_login: generic "login failed" interface function
306124208Sdes */
307124208Sdesvoid
308146998Sdesrecord_failed_login(const char *user, const char *hostname, const char *ttyname)
309124208Sdes{
310124208Sdes	if (geteuid() != 0)
311124208Sdes		return;
312124208Sdes
313124208Sdes	aix_setauthdb(user);
314126274Sdes#   ifdef AIX_LOGINFAILED_4ARG
315146998Sdes	loginfailed((char *)user, (char *)hostname, (char *)ttyname,
316146998Sdes	    AUDIT_FAIL_AUTH);
317126274Sdes#   else
318146998Sdes	loginfailed((char *)user, (char *)hostname, (char *)ttyname);
319126274Sdes#   endif
320126274Sdes	aix_restoreauthdb();
321124208Sdes}
322126274Sdes#  endif /* CUSTOM_FAILED_LOGIN */
323124208Sdes
324124208Sdes/*
325124208Sdes * If we have setauthdb, retrieve the password registry for the user's
326126274Sdes * account then feed it to setauthdb.  This will mean that subsequent AIX auth
327126274Sdes * functions will only use the specified loadable module.  If we don't have
328126274Sdes * setauthdb this is a no-op.
329124208Sdes */
330124208Sdesvoid
331124208Sdesaix_setauthdb(const char *user)
332124208Sdes{
333124208Sdes#  ifdef HAVE_SETAUTHDB
334126274Sdes	char *registry;
335124208Sdes
336124208Sdes	if (setuserdb(S_READ) == -1) {
337124208Sdes		debug3("%s: Could not open userdb to read", __func__);
338124208Sdes		return;
339124208Sdes	}
340323134Sdes
341124208Sdes	if (getuserattr((char *)user, S_REGISTRY, &registry, SEC_CHAR) == 0) {
342126274Sdes		if (setauthdb(registry, old_registry) == 0)
343126274Sdes			debug3("AIX/setauthdb set registry '%s'", registry);
344323134Sdes		else
345126274Sdes			debug3("AIX/setauthdb set registry '%s' failed: %s",
346126274Sdes			    registry, strerror(errno));
347124208Sdes	} else
348124208Sdes		debug3("%s: Could not read S_REGISTRY for user: %s", __func__,
349124208Sdes		    strerror(errno));
350124208Sdes	enduserdb();
351126274Sdes#  endif /* HAVE_SETAUTHDB */
352124208Sdes}
353126274Sdes
354126274Sdes/*
355126274Sdes * Restore the user's registry settings from old_registry.
356126274Sdes * Note that if the first aix_setauthdb fails, setauthdb("") is still safe
357126274Sdes * (it restores the system default behaviour).  If we don't have setauthdb,
358126274Sdes * this is a no-op.
359126274Sdes */
360126274Sdesvoid
361126274Sdesaix_restoreauthdb(void)
362126274Sdes{
363126274Sdes#  ifdef HAVE_SETAUTHDB
364126274Sdes	if (setauthdb(old_registry, NULL) == 0)
365126274Sdes		debug3("%s: restoring old registry '%s'", __func__,
366126274Sdes		    old_registry);
367126274Sdes	else
368126274Sdes		debug3("%s: failed to restore old registry %s", __func__,
369126274Sdes		    old_registry);
370126274Sdes#  endif /* HAVE_SETAUTHDB */
371126274Sdes}
372126274Sdes
373126274Sdes# endif /* WITH_AIXAUTHENTICATE */
374126274Sdes
375204917Sdes# ifdef USE_AIX_KRB_NAME
376204917Sdes/*
377204917Sdes * aix_krb5_get_principal_name: returns the user's kerberos client principal name if
378204917Sdes * configured, otherwise NULL.  Caller must free returned string.
379204917Sdes */
380204917Sdeschar *
381204917Sdesaix_krb5_get_principal_name(char *pw_name)
382204917Sdes{
383204917Sdes	char *authname = NULL, *authdomain = NULL, *principal = NULL;
384204917Sdes
385204917Sdes	setuserdb(S_READ);
386204917Sdes	if (getuserattr(pw_name, S_AUTHDOMAIN, &authdomain, SEC_CHAR) != 0)
387204917Sdes		debug("AIX getuserattr S_AUTHDOMAIN: %s", strerror(errno));
388204917Sdes	if (getuserattr(pw_name, S_AUTHNAME, &authname, SEC_CHAR) != 0)
389204917Sdes		debug("AIX getuserattr S_AUTHNAME: %s", strerror(errno));
390204917Sdes
391204917Sdes	if (authdomain != NULL)
392204917Sdes		xasprintf(&principal, "%s@%s", authname ? authname : pw_name, authdomain);
393204917Sdes	else if (authname != NULL)
394204917Sdes		principal = xstrdup(authname);
395204917Sdes	enduserdb();
396204917Sdes	return principal;
397204917Sdes}
398204917Sdes# endif /* USE_AIX_KRB_NAME */
399204917Sdes
400146998Sdes# if defined(AIX_GETNAMEINFO_HACK) && !defined(BROKEN_ADDRINFO)
401146998Sdes# undef getnameinfo
402146998Sdes/*
403146998Sdes * For some reason, AIX's getnameinfo will refuse to resolve the all-zeros
404146998Sdes * IPv6 address into its textual representation ("::"), so we wrap it
405146998Sdes * with a function that will.
406146998Sdes */
407146998Sdesint
408146998Sdessshaix_getnameinfo(const struct sockaddr *sa, size_t salen, char *host,
409146998Sdes    size_t hostlen, char *serv, size_t servlen, int flags)
410146998Sdes{
411146998Sdes	struct sockaddr_in6 *sa6;
412146998Sdes	u_int32_t *a6;
413146998Sdes
414146998Sdes	if (flags & (NI_NUMERICHOST|NI_NUMERICSERV) &&
415146998Sdes	    sa->sa_family == AF_INET6) {
416146998Sdes		sa6 = (struct sockaddr_in6 *)sa;
417146998Sdes		a6 = sa6->sin6_addr.u6_addr.u6_addr32;
418146998Sdes
419146998Sdes		if (a6[0] == 0 && a6[1] == 0 && a6[2] == 0 && a6[3] == 0) {
420146998Sdes			strlcpy(host, "::", hostlen);
421146998Sdes			snprintf(serv, servlen, "%d", sa6->sin6_port);
422146998Sdes			return 0;
423146998Sdes		}
424146998Sdes	}
425146998Sdes	return getnameinfo(sa, salen, host, hostlen, serv, servlen, flags);
426146998Sdes}
427146998Sdes# endif /* AIX_GETNAMEINFO_HACK */
428146998Sdes
429181111Sdes# if defined(USE_GETGRSET)
430181111Sdes#  include <stdlib.h>
431181111Sdesint
432181111Sdesgetgrouplist(const char *user, gid_t pgid, gid_t *groups, int *grpcnt)
433181111Sdes{
434181111Sdes	char *cp, *grplist, *grp;
435181111Sdes	gid_t gid;
436181111Sdes	int ret = 0, ngroups = 0, maxgroups;
437181111Sdes	long l;
438181111Sdes
439181111Sdes	maxgroups = *grpcnt;
440181111Sdes
441181111Sdes	if ((cp = grplist = getgrset(user)) == NULL)
442181111Sdes		return -1;
443181111Sdes
444181111Sdes	/* handle zero-length case */
445181111Sdes	if (maxgroups <= 0) {
446181111Sdes		*grpcnt = 0;
447181111Sdes		return -1;
448181111Sdes	}
449181111Sdes
450181111Sdes	/* copy primary group */
451181111Sdes	groups[ngroups++] = pgid;
452181111Sdes
453181111Sdes	/* copy each entry from getgrset into group list */
454181111Sdes	while ((grp = strsep(&grplist, ",")) != NULL) {
455181111Sdes		l = strtol(grp, NULL, 10);
456181111Sdes		if (ngroups >= maxgroups || l == LONG_MIN || l == LONG_MAX) {
457181111Sdes			ret = -1;
458181111Sdes			goto out;
459181111Sdes		}
460181111Sdes		gid = (gid_t)l;
461181111Sdes		if (gid == pgid)
462181111Sdes			continue;	/* we have already added primary gid */
463181111Sdes		groups[ngroups++] = gid;
464181111Sdes	}
465181111Sdesout:
466181111Sdes	free(cp);
467181111Sdes	*grpcnt = ngroups;
468181111Sdes	return ret;
469181111Sdes}
470181111Sdes# endif	/* USE_GETGRSET */
471181111Sdes
47298937Sdes#endif /* _AIX */
473