version 1.23, 2000/04/29 18:11:51 |
version 1.23.2.4, 2001/03/12 15:44:07 |
|
|
/* |
/* |
* |
|
* auth-rsa.c |
|
* |
|
* Author: Tatu Ylonen <ylo@cs.hut.fi> |
* Author: Tatu Ylonen <ylo@cs.hut.fi> |
* |
|
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
* All rights reserved |
* All rights reserved |
* |
|
* Created: Mon Mar 27 01:46:52 1995 ylo |
|
* |
|
* RSA-based authentication. This code determines whether to admit a login |
* RSA-based authentication. This code determines whether to admit a login |
* based on RSA authentication. This file also contains functions to check |
* based on RSA authentication. This file also contains functions to check |
* validity of the host key. |
* validity of the host key. |
* |
* |
|
* As far as I am concerned, the code I have written for this software |
|
* can be used freely for any purpose. Any derived versions of this |
|
* software must be clearly marked as such, and if the derived work is |
|
* incompatible with the protocol description in the RFC file, it must be |
|
* called by a name other than "ssh" or "Secure Shell". |
*/ |
*/ |
|
|
#include "includes.h" |
#include "includes.h" |
RCSID("$Id$"); |
RCSID("$OpenBSD$"); |
|
|
|
#include <openssl/rsa.h> |
|
#include <openssl/md5.h> |
|
|
#include "rsa.h" |
#include "rsa.h" |
#include "packet.h" |
#include "packet.h" |
#include "xmalloc.h" |
#include "xmalloc.h" |
#include "ssh.h" |
#include "ssh1.h" |
#include "mpaux.h" |
#include "mpaux.h" |
#include "uidswap.h" |
#include "uidswap.h" |
#include "match.h" |
#include "match.h" |
|
#include "auth-options.h" |
|
#include "pathnames.h" |
|
#include "log.h" |
#include "servconf.h" |
#include "servconf.h" |
|
#include "auth.h" |
|
|
#include <openssl/rsa.h> |
/* import */ |
#include <openssl/md5.h> |
extern ServerOptions options; |
|
|
/* Flags that may be set in authorized_keys options. */ |
|
extern int no_port_forwarding_flag; |
|
extern int no_agent_forwarding_flag; |
|
extern int no_x11_forwarding_flag; |
|
extern int no_pty_flag; |
|
extern char *forced_command; |
|
extern struct envstring *custom_environment; |
|
|
|
/* |
/* |
* Session identifier that is used to bind key exchange and authentication |
* Session identifier that is used to bind key exchange and authentication |
* responses to a particular session. |
* responses to a particular session. |
*/ |
*/ |
extern unsigned char session_id[16]; |
extern u_char session_id[16]; |
|
|
/* |
/* |
* The .ssh/authorized_keys file contains public keys, one per line, in the |
* The .ssh/authorized_keys file contains public keys, one per line, in the |
|
|
{ |
{ |
BIGNUM *challenge, *encrypted_challenge; |
BIGNUM *challenge, *encrypted_challenge; |
BN_CTX *ctx; |
BN_CTX *ctx; |
unsigned char buf[32], mdbuf[16], response[16]; |
u_char buf[32], mdbuf[16], response[16]; |
MD5_CTX md; |
MD5_CTX md; |
unsigned int i; |
u_int i; |
int plen, len; |
int plen, len; |
|
|
encrypted_challenge = BN_new(); |
encrypted_challenge = BN_new(); |
|
|
int |
int |
auth_rsa(struct passwd *pw, BIGNUM *client_n) |
auth_rsa(struct passwd *pw, BIGNUM *client_n) |
{ |
{ |
extern ServerOptions options; |
char line[8192], file[MAXPATHLEN]; |
char line[8192], file[1024]; |
|
int authenticated; |
int authenticated; |
unsigned int bits; |
u_int bits; |
FILE *f; |
FILE *f; |
unsigned long linenum = 0; |
u_long linenum = 0; |
struct stat st; |
struct stat st; |
RSA *pk; |
RSA *pk; |
|
|
|
/* no user given */ |
|
if (pw == NULL) |
|
return 0; |
|
|
/* Temporarily use the user's uid. */ |
/* Temporarily use the user's uid. */ |
temporarily_use_uid(pw->pw_uid); |
temporarily_use_uid(pw->pw_uid); |
|
|
/* The authorized keys. */ |
/* The authorized keys. */ |
snprintf(file, sizeof file, "%.500s/%.100s", pw->pw_dir, |
snprintf(file, sizeof file, "%.500s/%.100s", pw->pw_dir, |
SSH_USER_PERMITTED_KEYS); |
_PATH_SSH_USER_PERMITTED_KEYS); |
|
|
/* Fail quietly if file does not exist */ |
/* Fail quietly if file does not exist */ |
if (stat(file, &st) < 0) { |
if (stat(file, &st) < 0) { |
|
|
"bad ownership or modes for '%s'.", pw->pw_name, file); |
"bad ownership or modes for '%s'.", pw->pw_name, file); |
fail = 1; |
fail = 1; |
} else { |
} else { |
/* Check path to SSH_USER_PERMITTED_KEYS */ |
/* Check path to _PATH_SSH_USER_PERMITTED_KEYS */ |
int i; |
int i; |
static const char *check[] = { |
static const char *check[] = { |
"", SSH_USER_DIR, NULL |
"", _PATH_SSH_USER_DIR, NULL |
}; |
}; |
for (i = 0; check[i]; i++) { |
for (i = 0; check[i]; i++) { |
snprintf(line, sizeof line, "%.500s/%.100s", pw->pw_dir, check[i]); |
snprintf(line, sizeof line, "%.500s/%.100s", pw->pw_dir, check[i]); |
|
|
} |
} |
if (fail) { |
if (fail) { |
fclose(f); |
fclose(f); |
log(buf); |
log("%s",buf); |
packet_send_debug(buf); |
packet_send_debug("%s",buf); |
restore_uid(); |
restore_uid(); |
return 0; |
return 0; |
} |
} |
|
|
/* Parse the key from the line. */ |
/* Parse the key from the line. */ |
if (!auth_rsa_read_key(&cp, &bits, pk->e, pk->n)) { |
if (!auth_rsa_read_key(&cp, &bits, pk->e, pk->n)) { |
debug("%.100s, line %lu: bad key syntax", |
debug("%.100s, line %lu: bad key syntax", |
SSH_USER_PERMITTED_KEYS, linenum); |
file, linenum); |
packet_send_debug("%.100s, line %lu: bad key syntax", |
packet_send_debug("%.100s, line %lu: bad key syntax", |
SSH_USER_PERMITTED_KEYS, linenum); |
file, linenum); |
continue; |
continue; |
} |
} |
/* cp now points to the comment part. */ |
/* cp now points to the comment part. */ |
|
|
file, linenum, BN_num_bits(pk->n), bits); |
file, linenum, BN_num_bits(pk->n), bits); |
|
|
/* We have found the desired key. */ |
/* We have found the desired key. */ |
|
/* |
|
* If our options do not allow this key to be used, |
|
* do not send challenge. |
|
*/ |
|
if (!auth_parse_options(pw, options, file, linenum)) |
|
continue; |
|
|
/* Perform the challenge-response dialog for this key. */ |
/* Perform the challenge-response dialog for this key. */ |
if (!auth_rsa_challenge_dialog(pk)) { |
if (!auth_rsa_challenge_dialog(pk)) { |
|
|
* authenticated. Note that we have not yet processed the |
* authenticated. Note that we have not yet processed the |
* options; this will be reset if the options cause the |
* options; this will be reset if the options cause the |
* authentication to be rejected. |
* authentication to be rejected. |
*/ |
|
authenticated = 1; |
|
|
|
/* RSA part of authentication was accepted. Now process the options. */ |
|
if (options) { |
|
while (*options && *options != ' ' && *options != '\t') { |
|
cp = "no-port-forwarding"; |
|
if (strncmp(options, cp, strlen(cp)) == 0) { |
|
packet_send_debug("Port forwarding disabled."); |
|
no_port_forwarding_flag = 1; |
|
options += strlen(cp); |
|
goto next_option; |
|
} |
|
cp = "no-agent-forwarding"; |
|
if (strncmp(options, cp, strlen(cp)) == 0) { |
|
packet_send_debug("Agent forwarding disabled."); |
|
no_agent_forwarding_flag = 1; |
|
options += strlen(cp); |
|
goto next_option; |
|
} |
|
cp = "no-X11-forwarding"; |
|
if (strncmp(options, cp, strlen(cp)) == 0) { |
|
packet_send_debug("X11 forwarding disabled."); |
|
no_x11_forwarding_flag = 1; |
|
options += strlen(cp); |
|
goto next_option; |
|
} |
|
cp = "no-pty"; |
|
if (strncmp(options, cp, strlen(cp)) == 0) { |
|
packet_send_debug("Pty allocation disabled."); |
|
no_pty_flag = 1; |
|
options += strlen(cp); |
|
goto next_option; |
|
} |
|
cp = "command=\""; |
|
if (strncmp(options, cp, strlen(cp)) == 0) { |
|
int i; |
|
options += strlen(cp); |
|
forced_command = xmalloc(strlen(options) + 1); |
|
i = 0; |
|
while (*options) { |
|
if (*options == '"') |
|
break; |
|
if (*options == '\\' && options[1] == '"') { |
|
options += 2; |
|
forced_command[i++] = '"'; |
|
continue; |
|
} |
|
forced_command[i++] = *options++; |
|
} |
|
if (!*options) { |
|
debug("%.100s, line %lu: missing end quote", |
|
SSH_USER_PERMITTED_KEYS, linenum); |
|
packet_send_debug("%.100s, line %lu: missing end quote", |
|
SSH_USER_PERMITTED_KEYS, linenum); |
|
continue; |
|
} |
|
forced_command[i] = 0; |
|
packet_send_debug("Forced command: %.900s", forced_command); |
|
options++; |
|
goto next_option; |
|
} |
|
cp = "environment=\""; |
|
if (strncmp(options, cp, strlen(cp)) == 0) { |
|
int i; |
|
char *s; |
|
struct envstring *new_envstring; |
|
options += strlen(cp); |
|
s = xmalloc(strlen(options) + 1); |
|
i = 0; |
|
while (*options) { |
|
if (*options == '"') |
|
break; |
|
if (*options == '\\' && options[1] == '"') { |
|
options += 2; |
|
s[i++] = '"'; |
|
continue; |
|
} |
|
s[i++] = *options++; |
|
} |
|
if (!*options) { |
|
debug("%.100s, line %lu: missing end quote", |
|
SSH_USER_PERMITTED_KEYS, linenum); |
|
packet_send_debug("%.100s, line %lu: missing end quote", |
|
SSH_USER_PERMITTED_KEYS, linenum); |
|
continue; |
|
} |
|
s[i] = 0; |
|
packet_send_debug("Adding to environment: %.900s", s); |
|
debug("Adding to environment: %.900s", s); |
|
options++; |
|
new_envstring = xmalloc(sizeof(struct envstring)); |
|
new_envstring->s = s; |
|
new_envstring->next = custom_environment; |
|
custom_environment = new_envstring; |
|
goto next_option; |
|
} |
|
cp = "from=\""; |
|
if (strncmp(options, cp, strlen(cp)) == 0) { |
|
char *patterns = xmalloc(strlen(options) + 1); |
|
int i; |
|
options += strlen(cp); |
|
i = 0; |
|
while (*options) { |
|
if (*options == '"') |
|
break; |
|
if (*options == '\\' && options[1] == '"') { |
|
options += 2; |
|
patterns[i++] = '"'; |
|
continue; |
|
} |
|
patterns[i++] = *options++; |
|
} |
|
if (!*options) { |
|
debug("%.100s, line %lu: missing end quote", |
|
SSH_USER_PERMITTED_KEYS, linenum); |
|
packet_send_debug("%.100s, line %lu: missing end quote", |
|
SSH_USER_PERMITTED_KEYS, linenum); |
|
continue; |
|
} |
|
patterns[i] = 0; |
|
options++; |
|
if (!match_hostname(get_canonical_hostname(), patterns, |
|
strlen(patterns)) && |
|
!match_hostname(get_remote_ipaddr(), patterns, |
|
strlen(patterns))) { |
|
log("RSA authentication tried for %.100s with correct key but not from a permitted host (host=%.200s, ip=%.200s).", |
|
pw->pw_name, get_canonical_hostname(), |
|
get_remote_ipaddr()); |
|
packet_send_debug("Your host '%.200s' is not permitted to use this key for login.", |
|
get_canonical_hostname()); |
|
xfree(patterns); |
|
/* key invalid for this host, reset flags */ |
|
authenticated = 0; |
|
no_agent_forwarding_flag = 0; |
|
no_port_forwarding_flag = 0; |
|
no_pty_flag = 0; |
|
no_x11_forwarding_flag = 0; |
|
while (custom_environment) { |
|
struct envstring *ce = custom_environment; |
|
custom_environment = ce->next; |
|
xfree(ce->s); |
|
xfree(ce); |
|
} |
|
if (forced_command) { |
|
xfree(forced_command); |
|
forced_command = NULL; |
|
} |
|
break; |
|
} |
|
xfree(patterns); |
|
/* Host name matches. */ |
|
goto next_option; |
|
} |
|
bad_option: |
|
log("Bad options in %.100s file, line %lu: %.50s", |
|
SSH_USER_PERMITTED_KEYS, linenum, options); |
|
packet_send_debug("Bad options in %.100s file, line %lu: %.50s", |
|
SSH_USER_PERMITTED_KEYS, linenum, options); |
|
authenticated = 0; |
|
break; |
|
|
|
next_option: |
|
/* |
|
* Skip the comma, and move to the next option |
|
* (or break out if there are no more). |
|
*/ |
|
if (!*options) |
|
fatal("Bugs in auth-rsa.c option processing."); |
|
if (*options == ' ' || *options == '\t') |
|
break; /* End of options. */ |
|
if (*options != ',') |
|
goto bad_option; |
|
options++; |
|
/* Process the next option. */ |
|
continue; |
|
} |
|
} |
|
/* |
|
* Break out of the loop if authentication was successful; |
* Break out of the loop if authentication was successful; |
* otherwise continue searching. |
* otherwise continue searching. |
*/ |
*/ |
if (authenticated) |
authenticated = 1; |
break; |
break; |
} |
} |
|
|
/* Restore the privileged uid. */ |
/* Restore the privileged uid. */ |
|
|
|
|
if (authenticated) |
if (authenticated) |
packet_send_debug("RSA authentication accepted."); |
packet_send_debug("RSA authentication accepted."); |
|
else |
|
auth_clear_options(); |
|
|
/* Return authentication result. */ |
/* Return authentication result. */ |
return authenticated; |
return authenticated; |