mirror of
git://anongit.mindrot.org/openssh.git
synced 2024-12-27 12:22:09 +00:00
d783435315
[OVERVIEW atomicio.c atomicio.h auth-bsdauth.c auth-chall.c auth-krb5.c] [auth-options.c auth-options.h auth-passwd.c auth-rh-rsa.c auth-rhosts.c] [auth-rsa.c auth-skey.c auth.c auth.h auth1.c auth2-chall.c auth2-gss.c] [auth2-hostbased.c auth2-kbdint.c auth2-none.c auth2-passwd.c ] [auth2-pubkey.c auth2.c authfd.c authfd.h authfile.c bufaux.c bufbn.c] [buffer.c buffer.h canohost.c channels.c channels.h cipher-3des1.c] [cipher-bf1.c cipher-ctr.c cipher.c cleanup.c clientloop.c compat.c] [compress.c deattack.c dh.c dispatch.c dns.c dns.h fatal.c groupaccess.c] [groupaccess.h gss-genr.c gss-serv-krb5.c gss-serv.c hostfile.c kex.c] [kex.h kexdh.c kexdhc.c kexdhs.c kexgex.c kexgexc.c kexgexs.c key.c] [key.h log.c log.h mac.c match.c md-sha256.c misc.c misc.h moduli.c] [monitor.c monitor_fdpass.c monitor_mm.c monitor_mm.h monitor_wrap.c] [monitor_wrap.h msg.c nchan.c packet.c progressmeter.c readconf.c] [readconf.h readpass.c rsa.c scard.c scard.h scp.c servconf.c servconf.h] [serverloop.c session.c session.h sftp-client.c sftp-common.c] [sftp-common.h sftp-glob.c sftp-server.c sftp.c ssh-add.c ssh-agent.c] [ssh-dss.c ssh-gss.h ssh-keygen.c ssh-keyscan.c ssh-keysign.c ssh-rsa.c] [ssh.c ssh.h sshconnect.c sshconnect.h sshconnect1.c sshconnect2.c] [sshd.c sshlogin.c sshlogin.h sshpty.c sshpty.h sshtty.c ttymodes.c] [uidswap.c uidswap.h uuencode.c uuencode.h xmalloc.c xmalloc.h] [loginrec.c loginrec.h openbsd-compat/port-aix.c openbsd-compat/port-tun.h] almost entirely get rid of the culture of ".h files that include .h files" ok djm, sort of ok stevesk makes the pain stop in one easy step NB. portable commit contains everything *except* removing includes.h, as that will take a fair bit more work as we move headers that are required for portability workarounds to defines.h. (also, this step wasn't "easy")
385 lines
10 KiB
C
385 lines
10 KiB
C
/*
|
|
*
|
|
* Copyright (c) 2001 Gert Doering. All rights reserved.
|
|
* Copyright (c) 2003,2004,2005 Darren Tucker. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
*/
|
|
#include "includes.h"
|
|
|
|
#include "xmalloc.h"
|
|
#include "buffer.h"
|
|
#include "key.h"
|
|
#include "hostfile.h"
|
|
#include "auth.h"
|
|
#include "ssh.h"
|
|
#include "log.h"
|
|
|
|
#ifdef _AIX
|
|
|
|
#include <errno.h>
|
|
#if defined(HAVE_NETDB_H)
|
|
# include <netdb.h>
|
|
#endif
|
|
#include <uinfo.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sys/socket.h>
|
|
#include "port-aix.h"
|
|
|
|
# ifdef HAVE_SETAUTHDB
|
|
static char old_registry[REGISTRY_SIZE] = "";
|
|
# endif
|
|
|
|
/*
|
|
* AIX has a "usrinfo" area where logname and other stuff is stored -
|
|
* a few applications actually use this and die if it's not set
|
|
*
|
|
* NOTE: TTY= should be set, but since no one uses it and it's hard to
|
|
* acquire due to privsep code. We will just drop support.
|
|
*/
|
|
void
|
|
aix_usrinfo(struct passwd *pw)
|
|
{
|
|
u_int i;
|
|
size_t len;
|
|
char *cp;
|
|
|
|
len = sizeof("LOGNAME= NAME= ") + (2 * strlen(pw->pw_name));
|
|
cp = xmalloc(len);
|
|
|
|
i = snprintf(cp, len, "LOGNAME=%s%cNAME=%s%c", pw->pw_name, '\0',
|
|
pw->pw_name, '\0');
|
|
if (usrinfo(SETUINFO, cp, i) == -1)
|
|
fatal("Couldn't set usrinfo: %s", strerror(errno));
|
|
debug3("AIX/UsrInfo: set len %d", i);
|
|
|
|
xfree(cp);
|
|
}
|
|
|
|
# ifdef WITH_AIXAUTHENTICATE
|
|
/*
|
|
* Remove embedded newlines in string (if any).
|
|
* Used before logging messages returned by AIX authentication functions
|
|
* so the message is logged on one line.
|
|
*/
|
|
void
|
|
aix_remove_embedded_newlines(char *p)
|
|
{
|
|
if (p == NULL)
|
|
return;
|
|
|
|
for (; *p; p++) {
|
|
if (*p == '\n')
|
|
*p = ' ';
|
|
}
|
|
/* Remove trailing whitespace */
|
|
if (*--p == ' ')
|
|
*p = '\0';
|
|
}
|
|
|
|
/*
|
|
* Test specifically for the case where SYSTEM == NONE and AUTH1 contains
|
|
* anything other than NONE or SYSTEM, which indicates that the admin has
|
|
* configured the account for purely AUTH1-type authentication.
|
|
*
|
|
* Since authenticate() doesn't check AUTH1, and sshd can't sanely support
|
|
* AUTH1 itself, in such a case authenticate() will allow access without
|
|
* authentation, which is almost certainly not what the admin intends.
|
|
*
|
|
* (The native tools, eg login, will process the AUTH1 list in addition to
|
|
* the SYSTEM list by using ckuserID(), however ckuserID() and AUTH1 methods
|
|
* have been deprecated since AIX 4.2.x and would be very difficult for sshd
|
|
* to support.
|
|
*
|
|
* Returns 0 if an unsupportable combination is found, 1 otherwise.
|
|
*/
|
|
static int
|
|
aix_valid_authentications(const char *user)
|
|
{
|
|
char *auth1, *sys, *p;
|
|
int valid = 1;
|
|
|
|
if (getuserattr((char *)user, S_AUTHSYSTEM, &sys, SEC_CHAR) != 0) {
|
|
logit("Can't retrieve attribute SYSTEM for %s: %.100s",
|
|
user, strerror(errno));
|
|
return 0;
|
|
}
|
|
|
|
debug3("AIX SYSTEM attribute %s", sys);
|
|
if (strcmp(sys, "NONE") != 0)
|
|
return 1; /* not "NONE", so is OK */
|
|
|
|
if (getuserattr((char *)user, S_AUTH1, &auth1, SEC_LIST) != 0) {
|
|
logit("Can't retrieve attribute auth1 for %s: %.100s",
|
|
user, strerror(errno));
|
|
return 0;
|
|
}
|
|
|
|
p = auth1;
|
|
/* A SEC_LIST is concatenated strings, ending with two NULs. */
|
|
while (p[0] != '\0' && p[1] != '\0') {
|
|
debug3("AIX auth1 attribute list member %s", p);
|
|
if (strcmp(p, "NONE") != 0 && strcmp(p, "SYSTEM")) {
|
|
logit("Account %s has unsupported auth1 value '%s'",
|
|
user, p);
|
|
valid = 0;
|
|
}
|
|
p += strlen(p) + 1;
|
|
}
|
|
|
|
return (valid);
|
|
}
|
|
|
|
/*
|
|
* Do authentication via AIX's authenticate routine. We loop until the
|
|
* reenter parameter is 0, but normally authenticate is called only once.
|
|
*
|
|
* Note: this function returns 1 on success, whereas AIX's authenticate()
|
|
* returns 0.
|
|
*/
|
|
int
|
|
sys_auth_passwd(Authctxt *ctxt, const char *password)
|
|
{
|
|
char *authmsg = NULL, *msg = NULL, *name = ctxt->pw->pw_name;
|
|
int authsuccess = 0, expired, reenter, result;
|
|
|
|
do {
|
|
result = authenticate((char *)name, (char *)password, &reenter,
|
|
&authmsg);
|
|
aix_remove_embedded_newlines(authmsg);
|
|
debug3("AIX/authenticate result %d, authmsg %.100s", result,
|
|
authmsg);
|
|
} while (reenter);
|
|
|
|
if (!aix_valid_authentications(name))
|
|
result = -1;
|
|
|
|
if (result == 0) {
|
|
authsuccess = 1;
|
|
|
|
/*
|
|
* Record successful login. We don't have a pty yet, so just
|
|
* label the line as "ssh"
|
|
*/
|
|
aix_setauthdb(name);
|
|
|
|
/*
|
|
* Check if the user's password is expired.
|
|
*/
|
|
expired = passwdexpired(name, &msg);
|
|
if (msg && *msg) {
|
|
buffer_append(ctxt->loginmsg, msg, strlen(msg));
|
|
aix_remove_embedded_newlines(msg);
|
|
}
|
|
debug3("AIX/passwdexpired returned %d msg %.100s", expired, msg);
|
|
|
|
switch (expired) {
|
|
case 0: /* password not expired */
|
|
break;
|
|
case 1: /* expired, password change required */
|
|
ctxt->force_pwchange = 1;
|
|
break;
|
|
default: /* user can't change(2) or other error (-1) */
|
|
logit("Password can't be changed for user %s: %.100s",
|
|
name, msg);
|
|
if (msg)
|
|
xfree(msg);
|
|
authsuccess = 0;
|
|
}
|
|
|
|
aix_restoreauthdb();
|
|
}
|
|
|
|
if (authmsg != NULL)
|
|
xfree(authmsg);
|
|
|
|
return authsuccess;
|
|
}
|
|
|
|
/*
|
|
* Check if specified account is permitted to log in.
|
|
* Returns 1 if login is allowed, 0 if not allowed.
|
|
*/
|
|
int
|
|
sys_auth_allowed_user(struct passwd *pw, Buffer *loginmsg)
|
|
{
|
|
char *msg = NULL;
|
|
int result, permitted = 0;
|
|
struct stat st;
|
|
|
|
/*
|
|
* Don't perform checks for root account (PermitRootLogin controls
|
|
* logins via * ssh) or if running as non-root user (since
|
|
* loginrestrictions will always fail due to insufficient privilege).
|
|
*/
|
|
if (pw->pw_uid == 0 || geteuid() != 0) {
|
|
debug3("%s: not checking", __func__);
|
|
return 1;
|
|
}
|
|
|
|
result = loginrestrictions(pw->pw_name, S_RLOGIN, NULL, &msg);
|
|
if (result == 0)
|
|
permitted = 1;
|
|
/*
|
|
* If restricted because /etc/nologin exists, the login will be denied
|
|
* in session.c after the nologin message is sent, so allow for now
|
|
* and do not append the returned message.
|
|
*/
|
|
if (result == -1 && errno == EPERM && stat(_PATH_NOLOGIN, &st) == 0)
|
|
permitted = 1;
|
|
else if (msg != NULL)
|
|
buffer_append(loginmsg, msg, strlen(msg));
|
|
if (msg == NULL)
|
|
msg = xstrdup("(none)");
|
|
aix_remove_embedded_newlines(msg);
|
|
debug3("AIX/loginrestrictions returned %d msg %.100s", result, msg);
|
|
|
|
if (!permitted)
|
|
logit("Login restricted for %s: %.100s", pw->pw_name, msg);
|
|
xfree(msg);
|
|
return permitted;
|
|
}
|
|
|
|
int
|
|
sys_auth_record_login(const char *user, const char *host, const char *ttynm,
|
|
Buffer *loginmsg)
|
|
{
|
|
char *msg = NULL;
|
|
int success = 0;
|
|
|
|
aix_setauthdb(user);
|
|
if (loginsuccess((char *)user, (char *)host, (char *)ttynm, &msg) == 0) {
|
|
success = 1;
|
|
if (msg != NULL) {
|
|
debug("AIX/loginsuccess: msg %s", msg);
|
|
buffer_append(loginmsg, msg, strlen(msg));
|
|
xfree(msg);
|
|
}
|
|
}
|
|
aix_restoreauthdb();
|
|
return (success);
|
|
}
|
|
|
|
# ifdef CUSTOM_FAILED_LOGIN
|
|
/*
|
|
* record_failed_login: generic "login failed" interface function
|
|
*/
|
|
void
|
|
record_failed_login(const char *user, const char *hostname, const char *ttyname)
|
|
{
|
|
if (geteuid() != 0)
|
|
return;
|
|
|
|
aix_setauthdb(user);
|
|
# ifdef AIX_LOGINFAILED_4ARG
|
|
loginfailed((char *)user, (char *)hostname, (char *)ttyname,
|
|
AUDIT_FAIL_AUTH);
|
|
# else
|
|
loginfailed((char *)user, (char *)hostname, (char *)ttyname);
|
|
# endif
|
|
aix_restoreauthdb();
|
|
}
|
|
# endif /* CUSTOM_FAILED_LOGIN */
|
|
|
|
/*
|
|
* If we have setauthdb, retrieve the password registry for the user's
|
|
* account then feed it to setauthdb. This will mean that subsequent AIX auth
|
|
* functions will only use the specified loadable module. If we don't have
|
|
* setauthdb this is a no-op.
|
|
*/
|
|
void
|
|
aix_setauthdb(const char *user)
|
|
{
|
|
# ifdef HAVE_SETAUTHDB
|
|
char *registry;
|
|
|
|
if (setuserdb(S_READ) == -1) {
|
|
debug3("%s: Could not open userdb to read", __func__);
|
|
return;
|
|
}
|
|
|
|
if (getuserattr((char *)user, S_REGISTRY, ®istry, SEC_CHAR) == 0) {
|
|
if (setauthdb(registry, old_registry) == 0)
|
|
debug3("AIX/setauthdb set registry '%s'", registry);
|
|
else
|
|
debug3("AIX/setauthdb set registry '%s' failed: %s",
|
|
registry, strerror(errno));
|
|
} else
|
|
debug3("%s: Could not read S_REGISTRY for user: %s", __func__,
|
|
strerror(errno));
|
|
enduserdb();
|
|
# endif /* HAVE_SETAUTHDB */
|
|
}
|
|
|
|
/*
|
|
* Restore the user's registry settings from old_registry.
|
|
* Note that if the first aix_setauthdb fails, setauthdb("") is still safe
|
|
* (it restores the system default behaviour). If we don't have setauthdb,
|
|
* this is a no-op.
|
|
*/
|
|
void
|
|
aix_restoreauthdb(void)
|
|
{
|
|
# ifdef HAVE_SETAUTHDB
|
|
if (setauthdb(old_registry, NULL) == 0)
|
|
debug3("%s: restoring old registry '%s'", __func__,
|
|
old_registry);
|
|
else
|
|
debug3("%s: failed to restore old registry %s", __func__,
|
|
old_registry);
|
|
# endif /* HAVE_SETAUTHDB */
|
|
}
|
|
|
|
# endif /* WITH_AIXAUTHENTICATE */
|
|
|
|
# if defined(AIX_GETNAMEINFO_HACK) && !defined(BROKEN_ADDRINFO)
|
|
# undef getnameinfo
|
|
/*
|
|
* For some reason, AIX's getnameinfo will refuse to resolve the all-zeros
|
|
* IPv6 address into its textual representation ("::"), so we wrap it
|
|
* with a function that will.
|
|
*/
|
|
int
|
|
sshaix_getnameinfo(const struct sockaddr *sa, size_t salen, char *host,
|
|
size_t hostlen, char *serv, size_t servlen, int flags)
|
|
{
|
|
struct sockaddr_in6 *sa6;
|
|
u_int32_t *a6;
|
|
|
|
if (flags & (NI_NUMERICHOST|NI_NUMERICSERV) &&
|
|
sa->sa_family == AF_INET6) {
|
|
sa6 = (struct sockaddr_in6 *)sa;
|
|
a6 = sa6->sin6_addr.u6_addr.u6_addr32;
|
|
|
|
if (a6[0] == 0 && a6[1] == 0 && a6[2] == 0 && a6[3] == 0) {
|
|
strlcpy(host, "::", hostlen);
|
|
snprintf(serv, servlen, "%d", sa6->sin6_port);
|
|
return 0;
|
|
}
|
|
}
|
|
return getnameinfo(sa, salen, host, hostlen, serv, servlen, flags);
|
|
}
|
|
# endif /* AIX_GETNAMEINFO_HACK */
|
|
|
|
#endif /* _AIX */
|