auth2.c revision 65674
160573Skris/* 260573Skris * Copyright (c) 2000 Markus Friedl. All rights reserved. 360573Skris * 460573Skris * Redistribution and use in source and binary forms, with or without 560573Skris * modification, are permitted provided that the following conditions 660573Skris * are met: 760573Skris * 1. Redistributions of source code must retain the above copyright 860573Skris * notice, this list of conditions and the following disclaimer. 960573Skris * 2. Redistributions in binary form must reproduce the above copyright 1060573Skris * notice, this list of conditions and the following disclaimer in the 1160573Skris * documentation and/or other materials provided with the distribution. 1260573Skris * 1360573Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1460573Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1560573Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1660573Skris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1760573Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 1860573Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 1960573Skris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2060573Skris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2160573Skris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2260573Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2360573Skris */ 2465674Skris 2560573Skris#include "includes.h" 2665674SkrisRCSID("$OpenBSD: auth2.c,v 1.14 2000/09/07 20:27:49 deraadt Exp $"); 2765674SkrisRCSID("$FreeBSD: head/crypto/openssh/auth2.c 65674 2000-09-10 09:35:38Z kris $"); 2860573Skris 2960573Skris#include <openssl/dsa.h> 3060573Skris#include <openssl/rsa.h> 3160573Skris#include <openssl/evp.h> 3260573Skris 3360573Skris#include "xmalloc.h" 3460573Skris#include "rsa.h" 3560573Skris#include "ssh.h" 3660573Skris#include "pty.h" 3760573Skris#include "packet.h" 3860573Skris#include "buffer.h" 3960573Skris#include "cipher.h" 4060573Skris#include "servconf.h" 4160573Skris#include "compat.h" 4260573Skris#include "channels.h" 4360573Skris#include "bufaux.h" 4460573Skris#include "ssh2.h" 4560573Skris#include "auth.h" 4660573Skris#include "session.h" 4760573Skris#include "dispatch.h" 4860573Skris#include "auth.h" 4960573Skris#include "key.h" 5060573Skris#include "kex.h" 5160573Skris 5260573Skris#include "dsa.h" 5360573Skris#include "uidswap.h" 5465674Skris#include "auth-options.h" 5560573Skris 5660573Skris/* import */ 5760573Skrisextern ServerOptions options; 5860573Skrisextern unsigned char *session_id2; 5960573Skrisextern int session_id2_len; 6060573Skris 6160573Skris/* protocol */ 6260573Skris 6360573Skrisvoid input_service_request(int type, int plen); 6460573Skrisvoid input_userauth_request(int type, int plen); 6560573Skrisvoid protocol_error(int type, int plen); 6660573Skris 6760573Skris/* auth */ 6860573Skrisint ssh2_auth_none(struct passwd *pw); 6960573Skrisint ssh2_auth_password(struct passwd *pw); 7065674Skrisint ssh2_auth_pubkey(struct passwd *pw, char *service); 7160573Skris 7260573Skris/* helper */ 7360573Skrisstruct passwd* auth_set_user(char *u, char *s); 7460573Skrisint user_dsa_key_allowed(struct passwd *pw, Key *key); 7560573Skris 7660573Skristypedef struct Authctxt Authctxt; 7760573Skrisstruct Authctxt { 7860573Skris char *user; 7960573Skris char *service; 8060573Skris struct passwd pw; 8160573Skris int valid; 8260573Skris}; 8360573Skrisstatic Authctxt *authctxt = NULL; 8460573Skrisstatic int userauth_success = 0; 8560573Skris 8660573Skris/* 8760573Skris * loop until userauth_success == TRUE 8860573Skris */ 8960573Skris 9060573Skrisvoid 9160573Skrisdo_authentication2() 9260573Skris{ 9360573Skris /* turn off skey/kerberos, not supported by SSH2 */ 9460573Skris#ifdef SKEY 9560573Skris options.skey_authentication = 0; 9660573Skris#endif 9760573Skris#ifdef KRB4 9860576Skris options.krb4_authentication = 0; 9960573Skris#endif 10060573Skris 10160573Skris dispatch_init(&protocol_error); 10260573Skris dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request); 10360573Skris dispatch_run(DISPATCH_BLOCK, &userauth_success); 10460573Skris do_authenticated2(); 10560573Skris} 10660573Skris 10760573Skrisvoid 10860573Skrisprotocol_error(int type, int plen) 10960573Skris{ 11060573Skris log("auth: protocol error: type %d plen %d", type, plen); 11160573Skris packet_start(SSH2_MSG_UNIMPLEMENTED); 11260573Skris packet_put_int(0); 11360573Skris packet_send(); 11460573Skris packet_write_wait(); 11560573Skris} 11660573Skris 11760573Skrisvoid 11860573Skrisinput_service_request(int type, int plen) 11960573Skris{ 12060573Skris unsigned int len; 12160573Skris int accept = 0; 12260573Skris char *service = packet_get_string(&len); 12360573Skris packet_done(); 12460573Skris 12560573Skris if (strcmp(service, "ssh-userauth") == 0) { 12660573Skris if (!userauth_success) { 12760573Skris accept = 1; 12860573Skris /* now we can handle user-auth requests */ 12960573Skris dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &input_userauth_request); 13060573Skris } 13160573Skris } 13260573Skris /* XXX all other service requests are denied */ 13360573Skris 13460573Skris if (accept) { 13560573Skris packet_start(SSH2_MSG_SERVICE_ACCEPT); 13660573Skris packet_put_cstring(service); 13760573Skris packet_send(); 13860573Skris packet_write_wait(); 13960573Skris } else { 14060573Skris debug("bad service request %s", service); 14160573Skris packet_disconnect("bad service request %s", service); 14260573Skris } 14360573Skris xfree(service); 14460573Skris} 14560573Skris 14660573Skrisvoid 14760573Skrisinput_userauth_request(int type, int plen) 14860573Skris{ 14960573Skris static void (*authlog) (const char *fmt,...) = verbose; 15060573Skris static int attempt = 0; 15165674Skris unsigned int len; 15260573Skris int authenticated = 0; 15365674Skris char *user, *service, *method, *authmsg = NULL; 15460573Skris struct passwd *pw; 15560573Skris 15660573Skris if (++attempt == AUTH_FAIL_MAX) 15760573Skris packet_disconnect("too many failed userauth_requests"); 15860573Skris 15960573Skris user = packet_get_string(&len); 16060573Skris service = packet_get_string(&len); 16160573Skris method = packet_get_string(&len); 16260573Skris debug("userauth-request for user %s service %s method %s", user, service, method); 16360573Skris 16460573Skris /* XXX we only allow the ssh-connection service */ 16560573Skris pw = auth_set_user(user, service); 16660573Skris if (pw && strcmp(service, "ssh-connection")==0) { 16760573Skris if (strcmp(method, "none") == 0) { 16860573Skris authenticated = ssh2_auth_none(pw); 16960573Skris } else if (strcmp(method, "password") == 0) { 17060573Skris authenticated = ssh2_auth_password(pw); 17160573Skris } else if (strcmp(method, "publickey") == 0) { 17265674Skris authenticated = ssh2_auth_pubkey(pw, service); 17360573Skris } 17460573Skris } 17560573Skris if (authenticated && pw && pw->pw_uid == 0 && !options.permit_root_login) { 17660573Skris authenticated = 0; 17760573Skris log("ROOT LOGIN REFUSED FROM %.200s", 17860573Skris get_canonical_hostname()); 17960573Skris } 18060573Skris 18160573Skris /* Raise logging level */ 18260573Skris if (authenticated == 1 || 18360573Skris attempt == AUTH_FAIL_LOG || 18460573Skris strcmp(method, "password") == 0) 18560573Skris authlog = log; 18660573Skris 18760573Skris /* Log before sending the reply */ 18860573Skris if (authenticated == 1) { 18960573Skris authmsg = "Accepted"; 19060573Skris } else if (authenticated == 0) { 19160573Skris authmsg = "Failed"; 19260573Skris } else { 19360573Skris authmsg = "Postponed"; 19460573Skris } 19560573Skris authlog("%s %s for %.200s from %.200s port %d ssh2", 19660573Skris authmsg, 19760573Skris method, 19860573Skris pw && pw->pw_uid == 0 ? "ROOT" : user, 19960573Skris get_remote_ipaddr(), 20060573Skris get_remote_port()); 20160573Skris 20260573Skris /* XXX todo: check if multiple auth methods are needed */ 20360573Skris if (authenticated == 1) { 20460573Skris /* turn off userauth */ 20560573Skris dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &protocol_error); 20660573Skris packet_start(SSH2_MSG_USERAUTH_SUCCESS); 20760573Skris packet_send(); 20860573Skris packet_write_wait(); 20960573Skris /* now we can break out */ 21060573Skris userauth_success = 1; 21160573Skris } else if (authenticated == 0) { 21260573Skris packet_start(SSH2_MSG_USERAUTH_FAILURE); 21360573Skris packet_put_cstring("publickey,password"); /* XXX dynamic */ 21460573Skris packet_put_char(0); /* XXX partial success, unused */ 21560573Skris packet_send(); 21660573Skris packet_write_wait(); 21760573Skris } 21860573Skris 21960573Skris xfree(service); 22060573Skris xfree(user); 22160573Skris xfree(method); 22260573Skris} 22360573Skris 22460573Skrisint 22560573Skrisssh2_auth_none(struct passwd *pw) 22660573Skris{ 22760573Skris packet_done(); 22860573Skris return auth_password(pw, ""); 22960573Skris} 23060573Skrisint 23160573Skrisssh2_auth_password(struct passwd *pw) 23260573Skris{ 23360573Skris char *password; 23460573Skris int authenticated = 0; 23560573Skris int change; 23660573Skris unsigned int len; 23760573Skris change = packet_get_char(); 23860573Skris if (change) 23960573Skris log("password change not supported"); 24060573Skris password = packet_get_string(&len); 24160573Skris packet_done(); 24260573Skris if (options.password_authentication && 24360573Skris auth_password(pw, password) == 1) 24460573Skris authenticated = 1; 24560573Skris memset(password, 0, len); 24660573Skris xfree(password); 24760573Skris return authenticated; 24860573Skris} 24960573Skrisint 25065674Skrisssh2_auth_pubkey(struct passwd *pw, char *service) 25160573Skris{ 25260573Skris Buffer b; 25360573Skris Key *key; 25460573Skris char *pkalg, *pkblob, *sig; 25560573Skris unsigned int alen, blen, slen; 25660573Skris int have_sig; 25760573Skris int authenticated = 0; 25860573Skris 25960573Skris if (options.dsa_authentication == 0) { 26060573Skris debug("pubkey auth disabled"); 26160573Skris return 0; 26260573Skris } 26360573Skris have_sig = packet_get_char(); 26460573Skris pkalg = packet_get_string(&alen); 26560573Skris if (strcmp(pkalg, KEX_DSS) != 0) { 26660573Skris xfree(pkalg); 26760573Skris log("bad pkalg %s", pkalg); /*XXX*/ 26860573Skris return 0; 26960573Skris } 27060573Skris pkblob = packet_get_string(&blen); 27160573Skris key = dsa_key_from_blob(pkblob, blen); 27260573Skris if (key != NULL) { 27360573Skris if (have_sig) { 27460573Skris sig = packet_get_string(&slen); 27560573Skris packet_done(); 27660573Skris buffer_init(&b); 27765674Skris if (datafellows & SSH_COMPAT_SESSIONID_ENCODING) { 27865674Skris buffer_put_string(&b, session_id2, session_id2_len); 27965674Skris } else { 28065674Skris buffer_append(&b, session_id2, session_id2_len); 28165674Skris } 28265674Skris /* reconstruct packet */ 28360573Skris buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); 28465674Skris buffer_put_cstring(&b, pw->pw_name); 28565674Skris buffer_put_cstring(&b, 28665674Skris datafellows & SSH_BUG_PUBKEYAUTH ? 28765674Skris "ssh-userauth" : 28865674Skris service); 28965674Skris buffer_put_cstring(&b, "publickey"); 29065674Skris buffer_put_char(&b, have_sig); 29165674Skris buffer_put_cstring(&b, KEX_DSS); 29265674Skris buffer_put_string(&b, pkblob, blen); 29360573Skris#ifdef DEBUG_DSS 29460573Skris buffer_dump(&b); 29560573Skris#endif 29660573Skris /* test for correct signature */ 29760573Skris if (user_dsa_key_allowed(pw, key) && 29860573Skris dsa_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b)) == 1) 29960573Skris authenticated = 1; 30060573Skris buffer_clear(&b); 30160573Skris xfree(sig); 30260573Skris } else { 30360573Skris packet_done(); 30460573Skris debug("test key..."); 30560573Skris /* test whether pkalg/pkblob are acceptable */ 30660573Skris /* XXX fake reply and always send PK_OK ? */ 30760573Skris /* 30860573Skris * XXX this allows testing whether a user is allowed 30960573Skris * to login: if you happen to have a valid pubkey this 31060573Skris * message is sent. the message is NEVER sent at all 31160573Skris * if a user is not allowed to login. is this an 31260573Skris * issue? -markus 31360573Skris */ 31460573Skris if (user_dsa_key_allowed(pw, key)) { 31560573Skris packet_start(SSH2_MSG_USERAUTH_PK_OK); 31660573Skris packet_put_string(pkalg, alen); 31760573Skris packet_put_string(pkblob, blen); 31860573Skris packet_send(); 31960573Skris packet_write_wait(); 32060573Skris authenticated = -1; 32160573Skris } 32260573Skris } 32360573Skris key_free(key); 32460573Skris } 32560573Skris xfree(pkalg); 32660573Skris xfree(pkblob); 32760573Skris return authenticated; 32860573Skris} 32960573Skris 33060573Skris/* set and get current user */ 33160573Skris 33260573Skrisstruct passwd* 33360573Skrisauth_get_user(void) 33460573Skris{ 33560573Skris return (authctxt != NULL && authctxt->valid) ? &authctxt->pw : NULL; 33660573Skris} 33760573Skris 33860573Skrisstruct passwd* 33960573Skrisauth_set_user(char *u, char *s) 34060573Skris{ 34160573Skris struct passwd *pw, *copy; 34260573Skris 34360573Skris if (authctxt == NULL) { 34460573Skris authctxt = xmalloc(sizeof(*authctxt)); 34560573Skris authctxt->valid = 0; 34660573Skris authctxt->user = xstrdup(u); 34760573Skris authctxt->service = xstrdup(s); 34860573Skris setproctitle("%s", u); 34960573Skris pw = getpwnam(u); 35060573Skris if (!pw || !allowed_user(pw)) { 35160573Skris log("auth_set_user: illegal user %s", u); 35260573Skris return NULL; 35360573Skris } 35460573Skris copy = &authctxt->pw; 35560573Skris memset(copy, 0, sizeof(*copy)); 35660573Skris copy->pw_name = xstrdup(pw->pw_name); 35760573Skris copy->pw_passwd = xstrdup(pw->pw_passwd); 35860573Skris copy->pw_uid = pw->pw_uid; 35960573Skris copy->pw_gid = pw->pw_gid; 36065674Skris copy->pw_class = xstrdup(pw->pw_class); 36160573Skris copy->pw_dir = xstrdup(pw->pw_dir); 36260573Skris copy->pw_shell = xstrdup(pw->pw_shell); 36362179Sgreen copy->pw_class = xstrdup(pw->pw_class); 36462179Sgreen copy->pw_expire = pw->pw_expire; 36562179Sgreen copy->pw_change = pw->pw_change; 36660573Skris authctxt->valid = 1; 36760573Skris } else { 36860573Skris if (strcmp(u, authctxt->user) != 0 || 36960573Skris strcmp(s, authctxt->service) != 0) { 37060573Skris log("auth_set_user: missmatch: (%s,%s)!=(%s,%s)", 37160573Skris u, s, authctxt->user, authctxt->service); 37260573Skris return NULL; 37360573Skris } 37460573Skris } 37560573Skris return auth_get_user(); 37660573Skris} 37760573Skris 37860573Skris/* return 1 if user allows given key */ 37960573Skrisint 38060573Skrisuser_dsa_key_allowed(struct passwd *pw, Key *key) 38160573Skris{ 38260573Skris char line[8192], file[1024]; 38360573Skris int found_key = 0; 38460573Skris unsigned int bits = -1; 38560573Skris FILE *f; 38660573Skris unsigned long linenum = 0; 38760573Skris struct stat st; 38860573Skris Key *found; 38960573Skris 39060573Skris /* Temporarily use the user's uid. */ 39160573Skris temporarily_use_uid(pw->pw_uid); 39260573Skris 39360573Skris /* The authorized keys. */ 39460573Skris snprintf(file, sizeof file, "%.500s/%.100s", pw->pw_dir, 39560573Skris SSH_USER_PERMITTED_KEYS2); 39660573Skris 39760573Skris /* Fail quietly if file does not exist */ 39860573Skris if (stat(file, &st) < 0) { 39960573Skris /* Restore the privileged uid. */ 40060573Skris restore_uid(); 40160573Skris return 0; 40260573Skris } 40360573Skris /* Open the file containing the authorized keys. */ 40460573Skris f = fopen(file, "r"); 40560573Skris if (!f) { 40660573Skris /* Restore the privileged uid. */ 40760573Skris restore_uid(); 40860573Skris return 0; 40960573Skris } 41060573Skris if (options.strict_modes) { 41160573Skris int fail = 0; 41260573Skris char buf[1024]; 41360573Skris /* Check open file in order to avoid open/stat races */ 41460573Skris if (fstat(fileno(f), &st) < 0 || 41560573Skris (st.st_uid != 0 && st.st_uid != pw->pw_uid) || 41660573Skris (st.st_mode & 022) != 0) { 41760573Skris snprintf(buf, sizeof buf, "DSA authentication refused for %.100s: " 41860573Skris "bad ownership or modes for '%s'.", pw->pw_name, file); 41960573Skris fail = 1; 42060573Skris } else { 42160573Skris /* Check path to SSH_USER_PERMITTED_KEYS */ 42260573Skris int i; 42360573Skris static const char *check[] = { 42460573Skris "", SSH_USER_DIR, NULL 42560573Skris }; 42660573Skris for (i = 0; check[i]; i++) { 42760573Skris snprintf(line, sizeof line, "%.500s/%.100s", 42860573Skris pw->pw_dir, check[i]); 42960573Skris if (stat(line, &st) < 0 || 43060573Skris (st.st_uid != 0 && st.st_uid != pw->pw_uid) || 43160573Skris (st.st_mode & 022) != 0) { 43260573Skris snprintf(buf, sizeof buf, 43360573Skris "DSA authentication refused for %.100s: " 43460573Skris "bad ownership or modes for '%s'.", 43560573Skris pw->pw_name, line); 43660573Skris fail = 1; 43760573Skris break; 43860573Skris } 43960573Skris } 44060573Skris } 44160573Skris if (fail) { 44260573Skris fclose(f); 44365674Skris log("%s",buf); 44460573Skris restore_uid(); 44560573Skris return 0; 44660573Skris } 44760573Skris } 44860573Skris found_key = 0; 44960573Skris found = key_new(KEY_DSA); 45060573Skris 45160573Skris while (fgets(line, sizeof(line), f)) { 45265674Skris char *cp, *options = NULL; 45360573Skris linenum++; 45460573Skris /* Skip leading whitespace, empty and comment lines. */ 45560573Skris for (cp = line; *cp == ' ' || *cp == '\t'; cp++) 45660573Skris ; 45760573Skris if (!*cp || *cp == '\n' || *cp == '#') 45860573Skris continue; 45965674Skris 46060573Skris bits = key_read(found, &cp); 46165674Skris if (bits == 0) { 46265674Skris /* no key? check if there are options for this key */ 46365674Skris int quoted = 0; 46465674Skris options = cp; 46565674Skris for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { 46665674Skris if (*cp == '\\' && cp[1] == '"') 46765674Skris cp++; /* Skip both */ 46865674Skris else if (*cp == '"') 46965674Skris quoted = !quoted; 47065674Skris } 47165674Skris /* Skip remaining whitespace. */ 47265674Skris for (; *cp == ' ' || *cp == '\t'; cp++) 47365674Skris ; 47465674Skris bits = key_read(found, &cp); 47565674Skris if (bits == 0) { 47665674Skris /* still no key? advance to next line*/ 47765674Skris continue; 47865674Skris } 47965674Skris } 48065674Skris if (key_equal(found, key) && 48165674Skris auth_parse_options(pw, options, linenum) == 1) { 48260573Skris found_key = 1; 48360573Skris debug("matching key found: file %s, line %ld", 48460573Skris file, linenum); 48560573Skris break; 48660573Skris } 48760573Skris } 48860573Skris restore_uid(); 48960573Skris fclose(f); 49060573Skris key_free(found); 49160573Skris return found_key; 49260573Skris} 493