auth2.c revision 60573
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 * 3. All advertising materials mentioning features or use of this software 1360573Skris * must display the following acknowledgement: 1460573Skris * This product includes software developed by Markus Friedl. 1560573Skris * 4. The name of the author may not be used to endorse or promote products 1660573Skris * derived from this software without specific prior written permission. 1760573Skris * 1860573Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1960573Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2060573Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2160573Skris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2260573Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2360573Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2460573Skris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2560573Skris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2660573Skris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2760573Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2860573Skris */ 2960573Skris#include "includes.h" 3060573SkrisRCSID("$OpenBSD: auth2.c,v 1.8 2000/05/08 17:42:24 markus Exp $"); 3160573Skris 3260573Skris#include <openssl/dsa.h> 3360573Skris#include <openssl/rsa.h> 3460573Skris#include <openssl/evp.h> 3560573Skris 3660573Skris#include "xmalloc.h" 3760573Skris#include "rsa.h" 3860573Skris#include "ssh.h" 3960573Skris#include "pty.h" 4060573Skris#include "packet.h" 4160573Skris#include "buffer.h" 4260573Skris#include "cipher.h" 4360573Skris#include "servconf.h" 4460573Skris#include "compat.h" 4560573Skris#include "channels.h" 4660573Skris#include "bufaux.h" 4760573Skris#include "ssh2.h" 4860573Skris#include "auth.h" 4960573Skris#include "session.h" 5060573Skris#include "dispatch.h" 5160573Skris#include "auth.h" 5260573Skris#include "key.h" 5360573Skris#include "kex.h" 5460573Skris 5560573Skris#include "dsa.h" 5660573Skris#include "uidswap.h" 5760573Skris 5860573Skris/* import */ 5960573Skrisextern ServerOptions options; 6060573Skrisextern unsigned char *session_id2; 6160573Skrisextern int session_id2_len; 6260573Skris 6360573Skris/* protocol */ 6460573Skris 6560573Skrisvoid input_service_request(int type, int plen); 6660573Skrisvoid input_userauth_request(int type, int plen); 6760573Skrisvoid protocol_error(int type, int plen); 6860573Skris 6960573Skris/* auth */ 7060573Skrisint ssh2_auth_none(struct passwd *pw); 7160573Skrisint ssh2_auth_password(struct passwd *pw); 7260573Skrisint ssh2_auth_pubkey(struct passwd *pw, unsigned char *raw, unsigned int rlen); 7360573Skris 7460573Skris/* helper */ 7560573Skrisstruct passwd* auth_set_user(char *u, char *s); 7660573Skrisint user_dsa_key_allowed(struct passwd *pw, Key *key); 7760573Skris 7860573Skristypedef struct Authctxt Authctxt; 7960573Skrisstruct Authctxt { 8060573Skris char *user; 8160573Skris char *service; 8260573Skris struct passwd pw; 8360573Skris int valid; 8460573Skris}; 8560573Skrisstatic Authctxt *authctxt = NULL; 8660573Skrisstatic int userauth_success = 0; 8760573Skris 8860573Skris/* 8960573Skris * loop until userauth_success == TRUE 9060573Skris */ 9160573Skris 9260573Skrisvoid 9360573Skrisdo_authentication2() 9460573Skris{ 9560573Skris /* turn off skey/kerberos, not supported by SSH2 */ 9660573Skris#ifdef SKEY 9760573Skris options.skey_authentication = 0; 9860573Skris#endif 9960573Skris#ifdef KRB4 10060573Skris options.kerberos_authentication = 0; 10160573Skris#endif 10260573Skris 10360573Skris dispatch_init(&protocol_error); 10460573Skris dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request); 10560573Skris dispatch_run(DISPATCH_BLOCK, &userauth_success); 10660573Skris do_authenticated2(); 10760573Skris} 10860573Skris 10960573Skrisvoid 11060573Skrisprotocol_error(int type, int plen) 11160573Skris{ 11260573Skris log("auth: protocol error: type %d plen %d", type, plen); 11360573Skris packet_start(SSH2_MSG_UNIMPLEMENTED); 11460573Skris packet_put_int(0); 11560573Skris packet_send(); 11660573Skris packet_write_wait(); 11760573Skris} 11860573Skris 11960573Skrisvoid 12060573Skrisinput_service_request(int type, int plen) 12160573Skris{ 12260573Skris unsigned int len; 12360573Skris int accept = 0; 12460573Skris char *service = packet_get_string(&len); 12560573Skris packet_done(); 12660573Skris 12760573Skris if (strcmp(service, "ssh-userauth") == 0) { 12860573Skris if (!userauth_success) { 12960573Skris accept = 1; 13060573Skris /* now we can handle user-auth requests */ 13160573Skris dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &input_userauth_request); 13260573Skris } 13360573Skris } 13460573Skris /* XXX all other service requests are denied */ 13560573Skris 13660573Skris if (accept) { 13760573Skris packet_start(SSH2_MSG_SERVICE_ACCEPT); 13860573Skris packet_put_cstring(service); 13960573Skris packet_send(); 14060573Skris packet_write_wait(); 14160573Skris } else { 14260573Skris debug("bad service request %s", service); 14360573Skris packet_disconnect("bad service request %s", service); 14460573Skris } 14560573Skris xfree(service); 14660573Skris} 14760573Skris 14860573Skrisvoid 14960573Skrisinput_userauth_request(int type, int plen) 15060573Skris{ 15160573Skris static void (*authlog) (const char *fmt,...) = verbose; 15260573Skris static int attempt = 0; 15360573Skris unsigned int len, rlen; 15460573Skris int authenticated = 0; 15560573Skris char *raw, *user, *service, *method, *authmsg = NULL; 15660573Skris struct passwd *pw; 15760573Skris 15860573Skris if (++attempt == AUTH_FAIL_MAX) 15960573Skris packet_disconnect("too many failed userauth_requests"); 16060573Skris 16160573Skris raw = packet_get_raw(&rlen); 16260573Skris if (plen != rlen) 16360573Skris fatal("plen != rlen"); 16460573Skris user = packet_get_string(&len); 16560573Skris service = packet_get_string(&len); 16660573Skris method = packet_get_string(&len); 16760573Skris debug("userauth-request for user %s service %s method %s", user, service, method); 16860573Skris 16960573Skris /* XXX we only allow the ssh-connection service */ 17060573Skris pw = auth_set_user(user, service); 17160573Skris if (pw && strcmp(service, "ssh-connection")==0) { 17260573Skris if (strcmp(method, "none") == 0) { 17360573Skris authenticated = ssh2_auth_none(pw); 17460573Skris } else if (strcmp(method, "password") == 0) { 17560573Skris authenticated = ssh2_auth_password(pw); 17660573Skris } else if (strcmp(method, "publickey") == 0) { 17760573Skris authenticated = ssh2_auth_pubkey(pw, raw, rlen); 17860573Skris } 17960573Skris } 18060573Skris if (authenticated && pw && pw->pw_uid == 0 && !options.permit_root_login) { 18160573Skris authenticated = 0; 18260573Skris log("ROOT LOGIN REFUSED FROM %.200s", 18360573Skris get_canonical_hostname()); 18460573Skris } 18560573Skris 18660573Skris /* Raise logging level */ 18760573Skris if (authenticated == 1 || 18860573Skris attempt == AUTH_FAIL_LOG || 18960573Skris strcmp(method, "password") == 0) 19060573Skris authlog = log; 19160573Skris 19260573Skris /* Log before sending the reply */ 19360573Skris if (authenticated == 1) { 19460573Skris authmsg = "Accepted"; 19560573Skris } else if (authenticated == 0) { 19660573Skris authmsg = "Failed"; 19760573Skris } else { 19860573Skris authmsg = "Postponed"; 19960573Skris } 20060573Skris authlog("%s %s for %.200s from %.200s port %d ssh2", 20160573Skris authmsg, 20260573Skris method, 20360573Skris pw && pw->pw_uid == 0 ? "ROOT" : user, 20460573Skris get_remote_ipaddr(), 20560573Skris get_remote_port()); 20660573Skris 20760573Skris /* XXX todo: check if multiple auth methods are needed */ 20860573Skris if (authenticated == 1) { 20960573Skris /* turn off userauth */ 21060573Skris dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &protocol_error); 21160573Skris packet_start(SSH2_MSG_USERAUTH_SUCCESS); 21260573Skris packet_send(); 21360573Skris packet_write_wait(); 21460573Skris /* now we can break out */ 21560573Skris userauth_success = 1; 21660573Skris } else if (authenticated == 0) { 21760573Skris packet_start(SSH2_MSG_USERAUTH_FAILURE); 21860573Skris packet_put_cstring("publickey,password"); /* XXX dynamic */ 21960573Skris packet_put_char(0); /* XXX partial success, unused */ 22060573Skris packet_send(); 22160573Skris packet_write_wait(); 22260573Skris } 22360573Skris 22460573Skris xfree(service); 22560573Skris xfree(user); 22660573Skris xfree(method); 22760573Skris} 22860573Skris 22960573Skrisint 23060573Skrisssh2_auth_none(struct passwd *pw) 23160573Skris{ 23260573Skris packet_done(); 23360573Skris return auth_password(pw, ""); 23460573Skris} 23560573Skrisint 23660573Skrisssh2_auth_password(struct passwd *pw) 23760573Skris{ 23860573Skris char *password; 23960573Skris int authenticated = 0; 24060573Skris int change; 24160573Skris unsigned int len; 24260573Skris change = packet_get_char(); 24360573Skris if (change) 24460573Skris log("password change not supported"); 24560573Skris password = packet_get_string(&len); 24660573Skris packet_done(); 24760573Skris if (options.password_authentication && 24860573Skris auth_password(pw, password) == 1) 24960573Skris authenticated = 1; 25060573Skris memset(password, 0, len); 25160573Skris xfree(password); 25260573Skris return authenticated; 25360573Skris} 25460573Skrisint 25560573Skrisssh2_auth_pubkey(struct passwd *pw, unsigned char *raw, unsigned int rlen) 25660573Skris{ 25760573Skris Buffer b; 25860573Skris Key *key; 25960573Skris char *pkalg, *pkblob, *sig; 26060573Skris unsigned int alen, blen, slen; 26160573Skris int have_sig; 26260573Skris int authenticated = 0; 26360573Skris 26460573Skris if (options.dsa_authentication == 0) { 26560573Skris debug("pubkey auth disabled"); 26660573Skris return 0; 26760573Skris } 26860573Skris if (datafellows & SSH_BUG_PUBKEYAUTH) { 26960573Skris log("bug compatibility with ssh-2.0.13 pubkey not implemented"); 27060573Skris return 0; 27160573Skris } 27260573Skris have_sig = packet_get_char(); 27360573Skris pkalg = packet_get_string(&alen); 27460573Skris if (strcmp(pkalg, KEX_DSS) != 0) { 27560573Skris xfree(pkalg); 27660573Skris log("bad pkalg %s", pkalg); /*XXX*/ 27760573Skris return 0; 27860573Skris } 27960573Skris pkblob = packet_get_string(&blen); 28060573Skris key = dsa_key_from_blob(pkblob, blen); 28160573Skris if (key != NULL) { 28260573Skris if (have_sig) { 28360573Skris sig = packet_get_string(&slen); 28460573Skris packet_done(); 28560573Skris buffer_init(&b); 28660573Skris buffer_append(&b, session_id2, session_id2_len); 28760573Skris buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); 28860573Skris if (slen + 4 > rlen) 28960573Skris fatal("bad rlen/slen"); 29060573Skris buffer_append(&b, raw, rlen - slen - 4); 29160573Skris#ifdef DEBUG_DSS 29260573Skris buffer_dump(&b); 29360573Skris#endif 29460573Skris /* test for correct signature */ 29560573Skris if (user_dsa_key_allowed(pw, key) && 29660573Skris dsa_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b)) == 1) 29760573Skris authenticated = 1; 29860573Skris buffer_clear(&b); 29960573Skris xfree(sig); 30060573Skris } else { 30160573Skris packet_done(); 30260573Skris debug("test key..."); 30360573Skris /* test whether pkalg/pkblob are acceptable */ 30460573Skris /* XXX fake reply and always send PK_OK ? */ 30560573Skris /* 30660573Skris * XXX this allows testing whether a user is allowed 30760573Skris * to login: if you happen to have a valid pubkey this 30860573Skris * message is sent. the message is NEVER sent at all 30960573Skris * if a user is not allowed to login. is this an 31060573Skris * issue? -markus 31160573Skris */ 31260573Skris if (user_dsa_key_allowed(pw, key)) { 31360573Skris packet_start(SSH2_MSG_USERAUTH_PK_OK); 31460573Skris packet_put_string(pkalg, alen); 31560573Skris packet_put_string(pkblob, blen); 31660573Skris packet_send(); 31760573Skris packet_write_wait(); 31860573Skris authenticated = -1; 31960573Skris } 32060573Skris } 32160573Skris key_free(key); 32260573Skris } 32360573Skris xfree(pkalg); 32460573Skris xfree(pkblob); 32560573Skris return authenticated; 32660573Skris} 32760573Skris 32860573Skris/* set and get current user */ 32960573Skris 33060573Skrisstruct passwd* 33160573Skrisauth_get_user(void) 33260573Skris{ 33360573Skris return (authctxt != NULL && authctxt->valid) ? &authctxt->pw : NULL; 33460573Skris} 33560573Skris 33660573Skrisstruct passwd* 33760573Skrisauth_set_user(char *u, char *s) 33860573Skris{ 33960573Skris struct passwd *pw, *copy; 34060573Skris 34160573Skris if (authctxt == NULL) { 34260573Skris authctxt = xmalloc(sizeof(*authctxt)); 34360573Skris authctxt->valid = 0; 34460573Skris authctxt->user = xstrdup(u); 34560573Skris authctxt->service = xstrdup(s); 34660573Skris setproctitle("%s", u); 34760573Skris pw = getpwnam(u); 34860573Skris if (!pw || !allowed_user(pw)) { 34960573Skris log("auth_set_user: illegal user %s", u); 35060573Skris return NULL; 35160573Skris } 35260573Skris copy = &authctxt->pw; 35360573Skris memset(copy, 0, sizeof(*copy)); 35460573Skris copy->pw_name = xstrdup(pw->pw_name); 35560573Skris copy->pw_passwd = xstrdup(pw->pw_passwd); 35660573Skris copy->pw_uid = pw->pw_uid; 35760573Skris copy->pw_gid = pw->pw_gid; 35860573Skris copy->pw_dir = xstrdup(pw->pw_dir); 35960573Skris copy->pw_shell = xstrdup(pw->pw_shell); 36060573Skris authctxt->valid = 1; 36160573Skris } else { 36260573Skris if (strcmp(u, authctxt->user) != 0 || 36360573Skris strcmp(s, authctxt->service) != 0) { 36460573Skris log("auth_set_user: missmatch: (%s,%s)!=(%s,%s)", 36560573Skris u, s, authctxt->user, authctxt->service); 36660573Skris return NULL; 36760573Skris } 36860573Skris } 36960573Skris return auth_get_user(); 37060573Skris} 37160573Skris 37260573Skris/* return 1 if user allows given key */ 37360573Skrisint 37460573Skrisuser_dsa_key_allowed(struct passwd *pw, Key *key) 37560573Skris{ 37660573Skris char line[8192], file[1024]; 37760573Skris int found_key = 0; 37860573Skris unsigned int bits = -1; 37960573Skris FILE *f; 38060573Skris unsigned long linenum = 0; 38160573Skris struct stat st; 38260573Skris Key *found; 38360573Skris 38460573Skris /* Temporarily use the user's uid. */ 38560573Skris temporarily_use_uid(pw->pw_uid); 38660573Skris 38760573Skris /* The authorized keys. */ 38860573Skris snprintf(file, sizeof file, "%.500s/%.100s", pw->pw_dir, 38960573Skris SSH_USER_PERMITTED_KEYS2); 39060573Skris 39160573Skris /* Fail quietly if file does not exist */ 39260573Skris if (stat(file, &st) < 0) { 39360573Skris /* Restore the privileged uid. */ 39460573Skris restore_uid(); 39560573Skris return 0; 39660573Skris } 39760573Skris /* Open the file containing the authorized keys. */ 39860573Skris f = fopen(file, "r"); 39960573Skris if (!f) { 40060573Skris /* Restore the privileged uid. */ 40160573Skris restore_uid(); 40260573Skris return 0; 40360573Skris } 40460573Skris if (options.strict_modes) { 40560573Skris int fail = 0; 40660573Skris char buf[1024]; 40760573Skris /* Check open file in order to avoid open/stat races */ 40860573Skris if (fstat(fileno(f), &st) < 0 || 40960573Skris (st.st_uid != 0 && st.st_uid != pw->pw_uid) || 41060573Skris (st.st_mode & 022) != 0) { 41160573Skris snprintf(buf, sizeof buf, "DSA authentication refused for %.100s: " 41260573Skris "bad ownership or modes for '%s'.", pw->pw_name, file); 41360573Skris fail = 1; 41460573Skris } else { 41560573Skris /* Check path to SSH_USER_PERMITTED_KEYS */ 41660573Skris int i; 41760573Skris static const char *check[] = { 41860573Skris "", SSH_USER_DIR, NULL 41960573Skris }; 42060573Skris for (i = 0; check[i]; i++) { 42160573Skris snprintf(line, sizeof line, "%.500s/%.100s", 42260573Skris pw->pw_dir, check[i]); 42360573Skris if (stat(line, &st) < 0 || 42460573Skris (st.st_uid != 0 && st.st_uid != pw->pw_uid) || 42560573Skris (st.st_mode & 022) != 0) { 42660573Skris snprintf(buf, sizeof buf, 42760573Skris "DSA authentication refused for %.100s: " 42860573Skris "bad ownership or modes for '%s'.", 42960573Skris pw->pw_name, line); 43060573Skris fail = 1; 43160573Skris break; 43260573Skris } 43360573Skris } 43460573Skris } 43560573Skris if (fail) { 43660573Skris log(buf); 43760573Skris fclose(f); 43860573Skris restore_uid(); 43960573Skris return 0; 44060573Skris } 44160573Skris } 44260573Skris found_key = 0; 44360573Skris found = key_new(KEY_DSA); 44460573Skris 44560573Skris while (fgets(line, sizeof(line), f)) { 44660573Skris char *cp; 44760573Skris linenum++; 44860573Skris /* Skip leading whitespace, empty and comment lines. */ 44960573Skris for (cp = line; *cp == ' ' || *cp == '\t'; cp++) 45060573Skris ; 45160573Skris if (!*cp || *cp == '\n' || *cp == '#') 45260573Skris continue; 45360573Skris bits = key_read(found, &cp); 45460573Skris if (bits == 0) 45560573Skris continue; 45660573Skris if (key_equal(found, key)) { 45760573Skris found_key = 1; 45860573Skris debug("matching key found: file %s, line %ld", 45960573Skris file, linenum); 46060573Skris break; 46160573Skris } 46260573Skris } 46360573Skris restore_uid(); 46460573Skris fclose(f); 46560573Skris key_free(found); 46660573Skris return found_key; 46760573Skris} 468