version 1.5.12.2, 2017/08/15 04:40:16 |
version 1.6, 2012/12/12 17:42:39 |
|
|
/* $NetBSD$ */ |
/* $NetBSD$ */ |
/* $OpenBSD: auth.c,v 1.119 2016/12/15 21:29:05 dtucker Exp $ */ |
/* $OpenBSD: auth.c,v 1.96 2012/05/13 01:42:32 dtucker Exp $ */ |
/* |
/* |
* Copyright (c) 2000 Markus Friedl. All rights reserved. |
* Copyright (c) 2000 Markus Friedl. All rights reserved. |
* |
* |
|
|
__RCSID("$NetBSD$"); |
__RCSID("$NetBSD$"); |
#include <sys/types.h> |
#include <sys/types.h> |
#include <sys/stat.h> |
#include <sys/stat.h> |
#include <sys/socket.h> |
#include <sys/param.h> |
|
|
#include <errno.h> |
#include <errno.h> |
#include <fcntl.h> |
#include <fcntl.h> |
Line 40 __RCSID("$NetBSD$"); |
|
Line 40 __RCSID("$NetBSD$"); |
|
#include <stdio.h> |
#include <stdio.h> |
#include <string.h> |
#include <string.h> |
#include <unistd.h> |
#include <unistd.h> |
#include <limits.h> |
|
#include <netdb.h> |
|
|
|
#include "xmalloc.h" |
#include "xmalloc.h" |
#include "match.h" |
#include "match.h" |
#include "groupaccess.h" |
#include "groupaccess.h" |
#include "log.h" |
#include "log.h" |
#include "buffer.h" |
#include "buffer.h" |
#include "misc.h" |
|
#include "servconf.h" |
#include "servconf.h" |
#include "key.h" |
#include "key.h" |
#include "hostfile.h" |
#include "hostfile.h" |
Line 56 __RCSID("$NetBSD$"); |
|
Line 53 __RCSID("$NetBSD$"); |
|
#include "auth-options.h" |
#include "auth-options.h" |
#include "canohost.h" |
#include "canohost.h" |
#include "uidswap.h" |
#include "uidswap.h" |
|
#include "misc.h" |
#include "packet.h" |
#include "packet.h" |
#ifdef GSSAPI |
#ifdef GSSAPI |
#include "ssh-gss.h" |
#include "ssh-gss.h" |
#endif |
#endif |
#include "authfile.h" |
#include "authfile.h" |
#include "monitor_wrap.h" |
#include "monitor_wrap.h" |
#include "authfile.h" |
|
#include "ssherr.h" |
|
#include "compat.h" |
|
#include "pfilter.h" |
|
|
|
#ifdef HAVE_LOGIN_CAP |
#ifdef HAVE_LOGIN_CAP |
#include <login_cap.h> |
#include <login_cap.h> |
Line 79 extern int use_privsep; |
|
Line 73 extern int use_privsep; |
|
Buffer auth_debug; |
Buffer auth_debug; |
int auth_debug_init; |
int auth_debug_init; |
|
|
#ifndef HOST_ONLY |
|
/* |
/* |
* Check if the user is allowed to log in via ssh. If user is listed |
* Check if the user is allowed to log in via ssh. If user is listed |
* in DenyUsers or one of user's groups is listed in DenyGroups, false |
* in DenyUsers or one of user's groups is listed in DenyGroups, false |
Line 97 allowed_user(struct passwd * pw) |
|
Line 90 allowed_user(struct passwd * pw) |
|
int match_name, match_ip; |
int match_name, match_ip; |
char *cap_hlist, *hp; |
char *cap_hlist, *hp; |
#endif |
#endif |
struct ssh *ssh = active_state; /* XXX */ |
|
struct stat st; |
struct stat st; |
const char *hostname = NULL, *ipaddr = NULL; |
const char *hostname = NULL, *ipaddr = NULL; |
int r; |
|
u_int i; |
u_int i; |
|
|
/* Shouldn't be called if pw is NULL, but better safe than sorry... */ |
/* Shouldn't be called if pw is NULL, but better safe than sorry... */ |
Line 108 allowed_user(struct passwd * pw) |
|
Line 99 allowed_user(struct passwd * pw) |
|
return 0; |
return 0; |
|
|
#ifdef HAVE_LOGIN_CAP |
#ifdef HAVE_LOGIN_CAP |
hostname = auth_get_canonical_hostname(ssh, options.use_dns); |
hostname = get_canonical_hostname(1); |
ipaddr = ssh_remote_ipaddr(ssh); |
ipaddr = get_remote_ipaddr(); |
|
|
lc = login_getclass(pw->pw_class); |
lc = login_getclass(pw->pw_class); |
|
|
Line 120 allowed_user(struct passwd * pw) |
|
Line 111 allowed_user(struct passwd * pw) |
|
if (cap_hlist != NULL) { |
if (cap_hlist != NULL) { |
hp = strtok(cap_hlist, ","); |
hp = strtok(cap_hlist, ","); |
while (hp != NULL) { |
while (hp != NULL) { |
match_name = match_hostname(hostname, hp); |
match_name = match_hostname(hostname, |
match_ip = match_hostname(ipaddr, hp); |
hp, strlen(hp)); |
|
match_ip = match_hostname(ipaddr, |
|
hp, strlen(hp)); |
/* |
/* |
* Only a positive match here causes a "deny". |
* Only a positive match here causes a "deny". |
*/ |
*/ |
Line 149 allowed_user(struct passwd * pw) |
|
Line 142 allowed_user(struct passwd * pw) |
|
return 0; |
return 0; |
} |
} |
while (hp != NULL) { |
while (hp != NULL) { |
match_name = match_hostname(hostname, hp); |
match_name = match_hostname(hostname, |
match_ip = match_hostname(ipaddr, hp); |
hp, strlen(hp)); |
|
match_ip = match_hostname(ipaddr, |
|
hp, strlen(hp)); |
/* |
/* |
* Negative match causes an immediate "deny". |
* Negative match causes an immediate "deny". |
* Positive match causes us to break out |
* Positive match causes us to break out |
Line 228 allowed_user(struct passwd * pw) |
|
Line 223 allowed_user(struct passwd * pw) |
|
if (stat(shell, &st) != 0) { |
if (stat(shell, &st) != 0) { |
logit("User %.100s not allowed because shell %.100s " |
logit("User %.100s not allowed because shell %.100s " |
"does not exist", pw->pw_name, shell); |
"does not exist", pw->pw_name, shell); |
free(shell); |
xfree(shell); |
return 0; |
return 0; |
} |
} |
if (S_ISREG(st.st_mode) == 0 || |
if (S_ISREG(st.st_mode) == 0 || |
(st.st_mode & (S_IXOTH|S_IXUSR|S_IXGRP)) == 0) { |
(st.st_mode & (S_IXOTH|S_IXUSR|S_IXGRP)) == 0) { |
logit("User %.100s not allowed because shell %.100s " |
logit("User %.100s not allowed because shell %.100s " |
"is not executable", pw->pw_name, shell); |
"is not executable", pw->pw_name, shell); |
free(shell); |
xfree(shell); |
return 0; |
return 0; |
} |
} |
free(shell); |
xfree(shell); |
} |
} |
/* |
/* |
* XXX Consider nuking {Allow,Deny}{Users,Groups}. We have the |
* XXX Consider nuking {Allow,Deny}{Users,Groups}. We have the |
Line 248 allowed_user(struct passwd * pw) |
|
Line 243 allowed_user(struct passwd * pw) |
|
|
|
if (options.num_deny_users > 0 || options.num_allow_users > 0 || |
if (options.num_deny_users > 0 || options.num_allow_users > 0 || |
options.num_deny_groups > 0 || options.num_allow_groups > 0) { |
options.num_deny_groups > 0 || options.num_allow_groups > 0) { |
hostname = auth_get_canonical_hostname(ssh, options.use_dns); |
hostname = get_canonical_hostname(options.use_dns); |
ipaddr = ssh_remote_ipaddr(ssh); |
ipaddr = get_remote_ipaddr(); |
} |
} |
|
|
/* Return false if user is listed in DenyUsers */ |
/* Return false if user is listed in DenyUsers */ |
if (options.num_deny_users > 0) { |
if (options.num_deny_users > 0) { |
for (i = 0; i < options.num_deny_users; i++) { |
for (i = 0; i < options.num_deny_users; i++) |
r = match_user(pw->pw_name, hostname, ipaddr, |
if (match_user(pw->pw_name, hostname, ipaddr, |
options.deny_users[i]); |
options.deny_users[i])) { |
if (r < 0) { |
|
fatal("Invalid DenyUsers pattern \"%.100s\"", |
|
options.deny_users[i]); |
|
} else if (r != 0) { |
|
logit("User %.100s from %.100s not allowed " |
logit("User %.100s from %.100s not allowed " |
"because listed in DenyUsers", |
"because listed in DenyUsers", |
pw->pw_name, hostname); |
pw->pw_name, hostname); |
return 0; |
return 0; |
} |
} |
} |
|
} |
} |
/* Return false if AllowUsers isn't empty and user isn't listed there */ |
/* Return false if AllowUsers isn't empty and user isn't listed there */ |
if (options.num_allow_users > 0) { |
if (options.num_allow_users > 0) { |
for (i = 0; i < options.num_allow_users; i++) { |
for (i = 0; i < options.num_allow_users; i++) |
r = match_user(pw->pw_name, hostname, ipaddr, |
if (match_user(pw->pw_name, hostname, ipaddr, |
options.allow_users[i]); |
options.allow_users[i])) |
if (r < 0) { |
|
fatal("Invalid AllowUsers pattern \"%.100s\"", |
|
options.allow_users[i]); |
|
} else if (r == 1) |
|
break; |
break; |
} |
|
/* i < options.num_allow_users iff we break for loop */ |
/* i < options.num_allow_users iff we break for loop */ |
if (i >= options.num_allow_users) { |
if (i >= options.num_allow_users) { |
logit("User %.100s from %.100s not allowed because " |
logit("User %.100s from %.100s not allowed because " |
Line 324 allowed_user(struct passwd * pw) |
|
Line 309 allowed_user(struct passwd * pw) |
|
} |
} |
|
|
void |
void |
auth_info(Authctxt *authctxt, const char *fmt, ...) |
auth_log(Authctxt *authctxt, int authenticated, const char *method, |
|
const char *info) |
{ |
{ |
va_list ap; |
|
int i; |
|
|
|
free(authctxt->info); |
|
authctxt->info = NULL; |
|
|
|
va_start(ap, fmt); |
|
i = vasprintf(&authctxt->info, fmt, ap); |
|
va_end(ap); |
|
|
|
if (i < 0 || authctxt->info == NULL) |
|
fatal("vasprintf failed"); |
|
} |
|
|
|
void |
|
auth_log(Authctxt *authctxt, int authenticated, int partial, |
|
const char *method, const char *submethod) |
|
{ |
|
struct ssh *ssh = active_state; /* XXX */ |
|
void (*authlog) (const char *fmt,...) = verbose; |
void (*authlog) (const char *fmt,...) = verbose; |
const char *authmsg; |
const char *authmsg; |
|
|
Line 360 auth_log(Authctxt *authctxt, int authent |
|
Line 327 auth_log(Authctxt *authctxt, int authent |
|
|
|
if (authctxt->postponed) |
if (authctxt->postponed) |
authmsg = "Postponed"; |
authmsg = "Postponed"; |
else if (partial) |
|
authmsg = "Partial"; |
|
else |
else |
authmsg = authenticated ? "Accepted" : "Failed"; |
authmsg = authenticated ? "Accepted" : "Failed"; |
|
|
authlog("%s %s%s%s for %s%.100s from %.200s port %d ssh2%s%s", |
authlog("%s %s for %s%.100s from %.200s port %d%s", |
authmsg, |
authmsg, |
method, |
method, |
submethod != NULL ? "/" : "", submethod == NULL ? "" : submethod, |
|
authctxt->valid ? "" : "invalid user ", |
|
authctxt->user, |
|
ssh_remote_ipaddr(ssh), |
|
ssh_remote_port(ssh), |
|
authctxt->info != NULL ? ": " : "", |
|
authctxt->info != NULL ? authctxt->info : ""); |
|
if (!authctxt->postponed) |
|
pfilter_notify(!authenticated); |
|
free(authctxt->info); |
|
authctxt->info = NULL; |
|
} |
|
|
|
void |
|
auth_maxtries_exceeded(Authctxt *authctxt) |
|
{ |
|
struct ssh *ssh = active_state; /* XXX */ |
|
|
|
error("maximum authentication attempts exceeded for " |
|
"%s%.100s from %.200s port %d ssh2", |
|
authctxt->valid ? "" : "invalid user ", |
authctxt->valid ? "" : "invalid user ", |
authctxt->user, |
authctxt->user, |
ssh_remote_ipaddr(ssh), |
get_remote_ipaddr(), |
ssh_remote_port(ssh)); |
get_remote_port(), |
packet_disconnect("Too many authentication failures"); |
info); |
/* NOTREACHED */ |
|
} |
} |
|
|
/* |
/* |
Line 402 auth_maxtries_exceeded(Authctxt *authctx |
|
Line 346 auth_maxtries_exceeded(Authctxt *authctx |
|
int |
int |
auth_root_allowed(const char *method) |
auth_root_allowed(const char *method) |
{ |
{ |
struct ssh *ssh = active_state; /* XXX */ |
|
|
|
switch (options.permit_root_login) { |
switch (options.permit_root_login) { |
case PERMIT_YES: |
case PERMIT_YES: |
return 1; |
return 1; |
case PERMIT_NO_PASSWD: |
case PERMIT_NO_PASSWD: |
if (strcmp(method, "publickey") == 0 || |
if (strcmp(method, "password") != 0) |
strcmp(method, "hostbased") == 0 || |
|
strcmp(method, "gssapi-with-mic") == 0) |
|
return 1; |
return 1; |
break; |
break; |
case PERMIT_FORCED_ONLY: |
case PERMIT_FORCED_ONLY: |
Line 420 auth_root_allowed(const char *method) |
|
Line 360 auth_root_allowed(const char *method) |
|
} |
} |
break; |
break; |
} |
} |
logit("ROOT LOGIN REFUSED FROM %.200s port %d", |
logit("ROOT LOGIN REFUSED FROM %.200s", get_remote_ipaddr()); |
ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); |
|
return 0; |
return 0; |
} |
} |
|
|
Line 436 auth_root_allowed(const char *method) |
|
Line 375 auth_root_allowed(const char *method) |
|
char * |
char * |
expand_authorized_keys(const char *filename, struct passwd *pw) |
expand_authorized_keys(const char *filename, struct passwd *pw) |
{ |
{ |
char *file, ret[PATH_MAX]; |
char *file, ret[MAXPATHLEN]; |
int i; |
int i; |
|
|
file = percent_expand(filename, "h", pw->pw_dir, |
file = percent_expand(filename, "h", pw->pw_dir, |
Line 452 expand_authorized_keys(const char *filen |
|
Line 391 expand_authorized_keys(const char *filen |
|
i = snprintf(ret, sizeof(ret), "%s/%s", pw->pw_dir, file); |
i = snprintf(ret, sizeof(ret), "%s/%s", pw->pw_dir, file); |
if (i < 0 || (size_t)i >= sizeof(ret)) |
if (i < 0 || (size_t)i >= sizeof(ret)) |
fatal("expand_authorized_keys: path too long"); |
fatal("expand_authorized_keys: path too long"); |
free(file); |
xfree(file); |
return (xstrdup(ret)); |
return (xstrdup(ret)); |
} |
} |
|
|
char * |
char * |
authorized_principals_file(struct passwd *pw) |
authorized_principals_file(struct passwd *pw) |
{ |
{ |
if (options.authorized_principals_file == NULL) |
if (options.authorized_principals_file == NULL || |
|
strcasecmp(options.authorized_principals_file, "none") == 0) |
return NULL; |
return NULL; |
return expand_authorized_keys(options.authorized_principals_file, pw); |
return expand_authorized_keys(options.authorized_principals_file, pw); |
} |
} |
Line 493 check_key_in_hostfiles(struct passwd *pw |
|
Line 433 check_key_in_hostfiles(struct passwd *pw |
|
load_hostkeys(hostkeys, host, user_hostfile); |
load_hostkeys(hostkeys, host, user_hostfile); |
restore_uid(); |
restore_uid(); |
} |
} |
free(user_hostfile); |
xfree(user_hostfile); |
} |
} |
host_status = check_key_in_hostkeys(hostkeys, key, &found); |
host_status = check_key_in_hostkeys(hostkeys, key, &found); |
if (host_status == HOST_REVOKED) |
if (host_status == HOST_REVOKED) |
Line 510 check_key_in_hostfiles(struct passwd *pw |
|
Line 450 check_key_in_hostfiles(struct passwd *pw |
|
return host_status; |
return host_status; |
} |
} |
|
|
|
|
/* |
/* |
* Check a given path for security. This is defined as all components |
* Check a given file for security. This is defined as all components |
* of the path to the file must be owned by either the owner of |
* of the path to the file must be owned by either the owner of |
* of the file or root and no directories must be group or world writable. |
* of the file or root and no directories must be group or world writable. |
* |
* |
* XXX Should any specific check be done for sym links ? |
* XXX Should any specific check be done for sym links ? |
* |
* |
* Takes a file name, its stat information (preferably from fstat() to |
* Takes an open file descriptor, the file name, a uid and and |
* avoid races), the uid of the expected owner, their home directory and an |
|
* error buffer plus max size as arguments. |
* error buffer plus max size as arguments. |
* |
* |
* Returns 0 on success and -1 on failure |
* Returns 0 on success and -1 on failure |
*/ |
*/ |
int |
static int |
auth_secure_path(const char *name, struct stat *stp, const char *pw_dir, |
secure_filename(FILE *f, const char *file, struct passwd *pw, |
uid_t uid, char *err, size_t errlen) |
char *err, size_t errlen) |
{ |
{ |
char buf[PATH_MAX], homedir[PATH_MAX]; |
uid_t uid = pw->pw_uid; |
|
char buf[MAXPATHLEN], homedir[MAXPATHLEN]; |
char *cp; |
char *cp; |
int comparehome = 0; |
int comparehome = 0; |
struct stat st; |
struct stat st; |
|
|
if (realpath(name, buf) == NULL) { |
if (realpath(file, buf) == NULL) { |
snprintf(err, errlen, "realpath %s failed: %s", name, |
snprintf(err, errlen, "realpath %s failed: %s", file, |
strerror(errno)); |
strerror(errno)); |
return -1; |
return -1; |
} |
} |
if (pw_dir != NULL && realpath(pw_dir, homedir) != NULL) |
if (realpath(pw->pw_dir, homedir) != NULL) |
comparehome = 1; |
comparehome = 1; |
|
|
if (!S_ISREG(stp->st_mode)) { |
/* check the open file to avoid races */ |
snprintf(err, errlen, "%s is not a regular file", buf); |
if (fstat(fileno(f), &st) < 0 || |
return -1; |
(st.st_uid != 0 && st.st_uid != uid) || |
} |
(st.st_mode & 022) != 0) { |
if ((stp->st_uid != 0 && stp->st_uid != uid) || |
|
(stp->st_mode & 022) != 0) { |
|
snprintf(err, errlen, "bad ownership or modes for file %s", |
snprintf(err, errlen, "bad ownership or modes for file %s", |
buf); |
buf); |
return -1; |
return -1; |
Line 581 auth_secure_path(const char *name, struc |
|
Line 520 auth_secure_path(const char *name, struc |
|
return 0; |
return 0; |
} |
} |
|
|
/* |
|
* Version of secure_path() that accepts an open file descriptor to |
|
* avoid races. |
|
* |
|
* Returns 0 on success and -1 on failure |
|
*/ |
|
static int |
|
secure_filename(FILE *f, const char *file, struct passwd *pw, |
|
char *err, size_t errlen) |
|
{ |
|
struct stat st; |
|
|
|
/* check the open file to avoid races */ |
|
if (fstat(fileno(f), &st) < 0) { |
|
snprintf(err, errlen, "cannot stat file %s: %s", |
|
file, strerror(errno)); |
|
return -1; |
|
} |
|
return auth_secure_path(file, &st, pw->pw_dir, pw->pw_uid, err, errlen); |
|
} |
|
|
|
static FILE * |
static FILE * |
auth_openfile(const char *file, struct passwd *pw, int strict_modes, |
auth_openfile(const char *file, struct passwd *pw, int strict_modes, |
int log_missing, const char *file_type) |
int log_missing, const char *file_type) |
Line 667 getpwnamallow(const char *user) |
|
Line 585 getpwnamallow(const char *user) |
|
auth_session_t *as; |
auth_session_t *as; |
#endif |
#endif |
#endif |
#endif |
struct ssh *ssh = active_state; /* XXX */ |
|
struct passwd *pw; |
struct passwd *pw; |
struct connection_info *ci = get_connection_info(1, options.use_dns); |
struct connection_info *ci = get_connection_info(1, options.use_dns); |
|
|
Line 676 getpwnamallow(const char *user) |
|
Line 593 getpwnamallow(const char *user) |
|
|
|
pw = getpwnam(user); |
pw = getpwnam(user); |
if (pw == NULL) { |
if (pw == NULL) { |
pfilter_notify(1); |
logit("Invalid user %.100s from %.100s", |
logit("Invalid user %.100s from %.100s port %d", |
user, get_remote_ipaddr()); |
user, ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); |
|
return (NULL); |
return (NULL); |
} |
} |
if (!allowed_user(pw)) |
if (!allowed_user(pw)) |
Line 707 getpwnamallow(const char *user) |
|
Line 623 getpwnamallow(const char *user) |
|
int |
int |
auth_key_is_revoked(Key *key) |
auth_key_is_revoked(Key *key) |
{ |
{ |
char *fp = NULL; |
char *key_fp; |
int r; |
|
|
|
if (options.revoked_keys_file == NULL) |
if (options.revoked_keys_file == NULL) |
return 0; |
return 0; |
if ((fp = sshkey_fingerprint(key, options.fingerprint_hash, |
|
SSH_FP_DEFAULT)) == NULL) { |
|
r = SSH_ERR_ALLOC_FAIL; |
|
error("%s: fingerprint key: %s", __func__, ssh_err(r)); |
|
goto out; |
|
} |
|
|
|
r = sshkey_check_revoked(key, options.revoked_keys_file); |
switch (key_in_file(key, options.revoked_keys_file, 0)) { |
switch (r) { |
|
case 0: |
case 0: |
break; /* not revoked */ |
/* key not revoked */ |
case SSH_ERR_KEY_REVOKED: |
return 0; |
error("Authentication key %s %s revoked by file %s", |
case -1: |
sshkey_type(key), fp, options.revoked_keys_file); |
/* Error opening revoked_keys_file: refuse all keys */ |
goto out; |
error("Revoked keys file is unreadable: refusing public key " |
default: |
"authentication"); |
error("Error checking authentication key %s %s in " |
return 1; |
"revoked keys file %s: %s", sshkey_type(key), fp, |
case 1: |
options.revoked_keys_file, ssh_err(r)); |
/* Key revoked */ |
goto out; |
key_fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); |
} |
error("WARNING: authentication attempt with a revoked " |
|
"%s key %s ", key_type(key), key_fp); |
/* Success */ |
xfree(key_fp); |
r = 0; |
return 1; |
|
} |
out: |
fatal("key_in_file returned junk"); |
free(fp); |
|
return r == 0 ? 0 : 1; |
|
} |
} |
|
|
void |
void |
Line 767 auth_debug_send(void) |
|
Line 673 auth_debug_send(void) |
|
while (buffer_len(&auth_debug)) { |
while (buffer_len(&auth_debug)) { |
msg = buffer_get_string(&auth_debug, NULL); |
msg = buffer_get_string(&auth_debug, NULL); |
packet_send_debug("%s", msg); |
packet_send_debug("%s", msg); |
free(msg); |
xfree(msg); |
} |
} |
} |
} |
|
|
|
|
|
|
return (&fake); |
return (&fake); |
} |
} |
#endif |
|
|
|
/* |
|
* Returns the remote DNS hostname as a string. The returned string must not |
|
* be freed. NB. this will usually trigger a DNS query the first time it is |
|
* called. |
|
* This function does additional checks on the hostname to mitigate some |
|
* attacks on legacy rhosts-style authentication. |
|
* XXX is RhostsRSAAuthentication vulnerable to these? |
|
* XXX Can we remove these checks? (or if not, remove RhostsRSAAuthentication?) |
|
*/ |
|
|
|
static char * |
|
remote_hostname(struct ssh *ssh) |
|
{ |
|
struct sockaddr_storage from; |
|
socklen_t fromlen; |
|
struct addrinfo hints, *ai, *aitop; |
|
char name[NI_MAXHOST], ntop2[NI_MAXHOST]; |
|
const char *ntop = ssh_remote_ipaddr(ssh); |
|
|
|
/* Get IP address of client. */ |
|
fromlen = sizeof(from); |
|
memset(&from, 0, sizeof(from)); |
|
if (getpeername(ssh_packet_get_connection_in(ssh), |
|
(struct sockaddr *)&from, &fromlen) < 0) { |
|
debug("getpeername failed: %.100s", strerror(errno)); |
|
return strdup(ntop); |
|
} |
|
|
|
debug3("Trying to reverse map address %.100s.", ntop); |
|
/* Map the IP address to a host name. */ |
|
if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name), |
|
NULL, 0, NI_NAMEREQD) != 0) { |
|
/* Host name not found. Use ip address. */ |
|
return strdup(ntop); |
|
} |
|
|
|
/* |
|
* if reverse lookup result looks like a numeric hostname, |
|
* someone is trying to trick us by PTR record like following: |
|
* 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5 |
|
*/ |
|
memset(&hints, 0, sizeof(hints)); |
|
hints.ai_socktype = SOCK_DGRAM; /*dummy*/ |
|
hints.ai_flags = AI_NUMERICHOST; |
|
if (getaddrinfo(name, NULL, &hints, &ai) == 0) { |
|
logit("Nasty PTR record \"%s\" is set up for %s, ignoring", |
|
name, ntop); |
|
freeaddrinfo(ai); |
|
return strdup(ntop); |
|
} |
|
|
|
/* Names are stored in lowercase. */ |
|
lowercase(name); |
|
|
|
/* |
|
* Map it back to an IP address and check that the given |
|
* address actually is an address of this host. This is |
|
* necessary because anyone with access to a name server can |
|
* define arbitrary names for an IP address. Mapping from |
|
* name to IP address can be trusted better (but can still be |
|
* fooled if the intruder has access to the name server of |
|
* the domain). |
|
*/ |
|
memset(&hints, 0, sizeof(hints)); |
|
hints.ai_family = from.ss_family; |
|
hints.ai_socktype = SOCK_STREAM; |
|
if (getaddrinfo(name, NULL, &hints, &aitop) != 0) { |
|
logit("reverse mapping checking getaddrinfo for %.700s " |
|
"[%s] failed.", name, ntop); |
|
return strdup(ntop); |
|
} |
|
/* Look for the address from the list of addresses. */ |
|
for (ai = aitop; ai; ai = ai->ai_next) { |
|
if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2, |
|
sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 && |
|
(strcmp(ntop, ntop2) == 0)) |
|
break; |
|
} |
|
freeaddrinfo(aitop); |
|
/* If we reached the end of the list, the address was not there. */ |
|
if (ai == NULL) { |
|
/* Address not found for the host name. */ |
|
logit("Address %.100s maps to %.600s, but this does not " |
|
"map back to the address.", ntop, name); |
|
return strdup(ntop); |
|
} |
|
return strdup(name); |
|
} |
|
|
|
/* |
|
* Return the canonical name of the host in the other side of the current |
|
* connection. The host name is cached, so it is efficient to call this |
|
* several times. |
|
*/ |
|
|
|
const char * |
|
auth_get_canonical_hostname(struct ssh *ssh, int use_dns) |
|
{ |
|
static char *dnsname; |
|
|
|
if (!use_dns) |
|
return ssh_remote_ipaddr(ssh); |
|
else if (dnsname != NULL) |
|
return dnsname; |
|
else { |
|
dnsname = remote_hostname(ssh); |
|
return dnsname; |
|
} |
|
} |
|