version 1.9, 2000/10/19 16:41:13 |
version 1.10, 2001/01/18 16:59:59 |
|
|
/* |
/* |
* Copyright (c) 1999,2000 Markus Friedl. All rights reserved. |
* Copyright (c) 2001 Markus Friedl. All rights reserved. |
* |
* |
* Redistribution and use in source and binary forms, with or without |
* Redistribution and use in source and binary forms, with or without |
* modification, are permitted provided that the following conditions |
* modification, are permitted provided that the following conditions |
|
|
RCSID("$OpenBSD$"); |
RCSID("$OpenBSD$"); |
|
|
#include "ssh.h" |
#include "ssh.h" |
#include "packet.h" |
#include "auth.h" |
#include <sha1.h> |
|
|
|
/* |
#ifdef SKEY |
* try skey authentication, |
char * |
* return 1 on success, 0 on failure, -1 if skey is not available |
get_challenge(Authctxt *authctxt, char *devs) |
*/ |
|
|
|
int |
|
auth_skey_password(struct passwd * pw, const char *password) |
|
{ |
{ |
if (strncasecmp(password, "s/key", 5) == 0) { |
static char challenge[1024]; |
char *skeyinfo = skey_keyinfo(pw->pw_name); |
struct skey skey; |
if (skeyinfo == NULL) { |
if (skeychallenge(&skey, authctxt->user, challenge) == -1) |
debug("generating fake skeyinfo for %.100s.", |
return NULL; |
pw->pw_name); |
strlcat(challenge, "\nS/Key Password: ", sizeof challenge); |
skeyinfo = skey_fake_keyinfo(pw->pw_name); |
return challenge; |
} |
|
if (skeyinfo != NULL) |
|
packet_send_debug("%s", skeyinfo); |
|
/* Try again. */ |
|
return 0; |
|
} else if (skey_haskey(pw->pw_name) == 0 && |
|
skey_passcheck(pw->pw_name, (char *) password) != -1) { |
|
/* Authentication succeeded. */ |
|
return 1; |
|
} |
|
/* Fall back to ordinary passwd authentication. */ |
|
return -1; |
|
} |
} |
|
int |
/* from %OpenBSD: skeylogin.c,v 1.32 1999/08/16 14:46:56 millert Exp % */ |
verify_response(Authctxt *authctxt, char *response) |
|
|
#define ROUND(x) (((x)[0] << 24) + (((x)[1]) << 16) + (((x)[2]) << 8) + \ |
|
((x)[3])) |
|
|
|
/* |
|
* hash_collapse() |
|
*/ |
|
static u_int32_t |
|
hash_collapse(s) |
|
u_char *s; |
|
{ |
{ |
int len, target; |
return (authctxt->valid && |
u_int32_t i; |
skey_haskey(authctxt->pw->pw_name) == 0 && |
|
skey_passcheck(authctxt->pw->pw_name, response) != -1); |
if ((strlen(s) % sizeof(u_int32_t)) == 0) |
|
target = strlen(s); /* Multiple of 4 */ |
|
else |
|
target = strlen(s) - (strlen(s) % sizeof(u_int32_t)); |
|
|
|
for (i = 0, len = 0; len < target; len += 4) |
|
i ^= ROUND(s + len); |
|
|
|
return i; |
|
} |
} |
|
#else |
|
/* not available */ |
char * |
char * |
skey_fake_keyinfo(char *username) |
get_challenge(Authctxt *authctxt, char *devs) |
{ |
{ |
int i; |
return NULL; |
u_int ptr; |
|
u_char hseed[SKEY_MAX_SEED_LEN], flg = 1, *up; |
|
char pbuf[SKEY_MAX_PW_LEN+1]; |
|
static char skeyprompt[SKEY_MAX_CHALLENGE+1]; |
|
char *secret = NULL; |
|
size_t secretlen = 0; |
|
SHA1_CTX ctx; |
|
char *p, *u; |
|
|
|
/* |
|
* Base first 4 chars of seed on hostname. |
|
* Add some filler for short hostnames if necessary. |
|
*/ |
|
if (gethostname(pbuf, sizeof(pbuf)) == -1) |
|
*(p = pbuf) = '.'; |
|
else |
|
for (p = pbuf; *p && isalnum(*p); p++) |
|
if (isalpha(*p) && isupper(*p)) |
|
*p = tolower(*p); |
|
if (*p && pbuf - p < 4) |
|
(void)strncpy(p, "asjd", 4 - (pbuf - p)); |
|
pbuf[4] = '\0'; |
|
|
|
/* Hash the username if possible */ |
|
if ((up = SHA1Data(username, strlen(username), NULL)) != NULL) { |
|
struct stat sb; |
|
time_t t; |
|
int fd; |
|
|
|
/* Collapse the hash */ |
|
ptr = hash_collapse(up); |
|
memset(up, 0, strlen(up)); |
|
|
|
/* See if the random file's there, else use ctime */ |
|
if ((fd = open(_SKEY_RAND_FILE_PATH_, O_RDONLY)) != -1 |
|
&& fstat(fd, &sb) == 0 && |
|
sb.st_size > (off_t)SKEY_MAX_SEED_LEN && |
|
lseek(fd, ptr % (sb.st_size - SKEY_MAX_SEED_LEN), |
|
SEEK_SET) != -1 && read(fd, hseed, |
|
SKEY_MAX_SEED_LEN) == SKEY_MAX_SEED_LEN) { |
|
close(fd); |
|
fd = -1; |
|
secret = hseed; |
|
secretlen = SKEY_MAX_SEED_LEN; |
|
flg = 0; |
|
} else if (!stat(_PATH_MEM, &sb) || !stat("/", &sb)) { |
|
t = sb.st_ctime; |
|
secret = ctime(&t); |
|
secretlen = strlen(secret); |
|
flg = 0; |
|
} |
|
if (fd != -1) |
|
close(fd); |
|
} |
|
|
|
/* Put that in your pipe and smoke it */ |
|
if (flg == 0) { |
|
/* Hash secret value with username */ |
|
SHA1Init(&ctx); |
|
SHA1Update(&ctx, secret, secretlen); |
|
SHA1Update(&ctx, username, strlen(username)); |
|
SHA1End(&ctx, up); |
|
|
|
/* Zero out */ |
|
memset(secret, 0, secretlen); |
|
|
|
/* Now hash the hash */ |
|
SHA1Init(&ctx); |
|
SHA1Update(&ctx, up, strlen(up)); |
|
SHA1End(&ctx, up); |
|
|
|
ptr = hash_collapse(up + 4); |
|
|
|
for (i = 4; i < 9; i++) { |
|
pbuf[i] = (ptr % 10) + '0'; |
|
ptr /= 10; |
|
} |
|
pbuf[i] = '\0'; |
|
|
|
/* Sequence number */ |
|
ptr = ((up[2] + up[3]) % 99) + 1; |
|
|
|
memset(up, 0, 20); /* SHA1 specific */ |
|
free(up); |
|
|
|
(void)snprintf(skeyprompt, sizeof skeyprompt, |
|
"otp-%.*s %d %.*s", |
|
SKEY_MAX_HASHNAME_LEN, |
|
skey_get_algorithm(), |
|
ptr, SKEY_MAX_SEED_LEN, |
|
pbuf); |
|
} else { |
|
/* Base last 8 chars of seed on username */ |
|
u = username; |
|
i = 8; |
|
p = &pbuf[4]; |
|
do { |
|
if (*u == 0) { |
|
/* Pad remainder with zeros */ |
|
while (--i >= 0) |
|
*p++ = '0'; |
|
break; |
|
} |
|
|
|
*p++ = (*u++ % 10) + '0'; |
|
} while (--i != 0); |
|
pbuf[12] = '\0'; |
|
|
|
(void)snprintf(skeyprompt, sizeof skeyprompt, |
|
"otp-%.*s %d %.*s", |
|
SKEY_MAX_HASHNAME_LEN, |
|
skey_get_algorithm(), |
|
99, SKEY_MAX_SEED_LEN, pbuf); |
|
} |
|
return skeyprompt; |
|
} |
} |
|
int |
|
verify_response(Authctxt *authctxt, char *response) |
|
{ |
|
return 0; |
|
} |
|
#endif |