10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * Copyright (c) 2000 Markus Friedl. All rights reserved. 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * Redistribution and use in source and binary forms, with or without 50Sstevel@tonic-gate * modification, are permitted provided that the following conditions 60Sstevel@tonic-gate * are met: 70Sstevel@tonic-gate * 1. Redistributions of source code must retain the above copyright 80Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer. 90Sstevel@tonic-gate * 2. Redistributions in binary form must reproduce the above copyright 100Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer in the 110Sstevel@tonic-gate * documentation and/or other materials provided with the distribution. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 140Sstevel@tonic-gate * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 150Sstevel@tonic-gate * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 160Sstevel@tonic-gate * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 170Sstevel@tonic-gate * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 180Sstevel@tonic-gate * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 190Sstevel@tonic-gate * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 200Sstevel@tonic-gate * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 210Sstevel@tonic-gate * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 220Sstevel@tonic-gate * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 230Sstevel@tonic-gate */ 240Sstevel@tonic-gate /* 25*5562Sjp161948 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 260Sstevel@tonic-gate * Use is subject to license terms. 270Sstevel@tonic-gate */ 280Sstevel@tonic-gate 290Sstevel@tonic-gate #include "includes.h" 300Sstevel@tonic-gate RCSID("$OpenBSD: auth2-pubkey.c,v 1.2 2002/05/31 11:35:15 markus Exp $"); 310Sstevel@tonic-gate 320Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 330Sstevel@tonic-gate 340Sstevel@tonic-gate #include "ssh2.h" 350Sstevel@tonic-gate #include "xmalloc.h" 360Sstevel@tonic-gate #include "packet.h" 370Sstevel@tonic-gate #include "buffer.h" 380Sstevel@tonic-gate #include "log.h" 390Sstevel@tonic-gate #include "servconf.h" 400Sstevel@tonic-gate #include "compat.h" 410Sstevel@tonic-gate #include "bufaux.h" 420Sstevel@tonic-gate #include "auth.h" 430Sstevel@tonic-gate #include "key.h" 440Sstevel@tonic-gate #include "pathnames.h" 450Sstevel@tonic-gate #include "uidswap.h" 460Sstevel@tonic-gate #include "auth-options.h" 470Sstevel@tonic-gate #include "canohost.h" 480Sstevel@tonic-gate 490Sstevel@tonic-gate #ifdef USE_PAM 500Sstevel@tonic-gate #include <security/pam_appl.h> 510Sstevel@tonic-gate #include "auth-pam.h" 520Sstevel@tonic-gate #endif /* USE_PAM */ 530Sstevel@tonic-gate 540Sstevel@tonic-gate /* import */ 550Sstevel@tonic-gate extern ServerOptions options; 560Sstevel@tonic-gate extern u_char *session_id2; 570Sstevel@tonic-gate extern int session_id2_len; 580Sstevel@tonic-gate 590Sstevel@tonic-gate static void 600Sstevel@tonic-gate userauth_pubkey(Authctxt *authctxt) 610Sstevel@tonic-gate { 620Sstevel@tonic-gate Buffer b; 630Sstevel@tonic-gate Key *key = NULL; 640Sstevel@tonic-gate char *pkalg; 650Sstevel@tonic-gate u_char *pkblob, *sig; 660Sstevel@tonic-gate u_int alen, blen, slen; 670Sstevel@tonic-gate int have_sig, pktype; 680Sstevel@tonic-gate int authenticated = 0; 690Sstevel@tonic-gate 700Sstevel@tonic-gate if (!authctxt || !authctxt->method) 710Sstevel@tonic-gate fatal("%s: missing context", __func__); 720Sstevel@tonic-gate 730Sstevel@tonic-gate have_sig = packet_get_char(); 740Sstevel@tonic-gate if (datafellows & SSH_BUG_PKAUTH) { 750Sstevel@tonic-gate debug2("userauth_pubkey: SSH_BUG_PKAUTH"); 760Sstevel@tonic-gate /* no explicit pkalg given */ 770Sstevel@tonic-gate pkblob = packet_get_string(&blen); 780Sstevel@tonic-gate buffer_init(&b); 790Sstevel@tonic-gate buffer_append(&b, pkblob, blen); 800Sstevel@tonic-gate /* so we have to extract the pkalg from the pkblob */ 810Sstevel@tonic-gate pkalg = buffer_get_string(&b, &alen); 820Sstevel@tonic-gate buffer_free(&b); 830Sstevel@tonic-gate } else { 840Sstevel@tonic-gate pkalg = packet_get_string(&alen); 850Sstevel@tonic-gate pkblob = packet_get_string(&blen); 860Sstevel@tonic-gate } 870Sstevel@tonic-gate pktype = key_type_from_name(pkalg); 880Sstevel@tonic-gate if (pktype == KEY_UNSPEC) { 890Sstevel@tonic-gate /* this is perfectly legal */ 900Sstevel@tonic-gate log("userauth_pubkey: unsupported public key algorithm: %s", 910Sstevel@tonic-gate pkalg); 920Sstevel@tonic-gate goto done; 930Sstevel@tonic-gate } 940Sstevel@tonic-gate key = key_from_blob(pkblob, blen); 950Sstevel@tonic-gate if (key == NULL) { 960Sstevel@tonic-gate error("userauth_pubkey: cannot decode key: %s", pkalg); 970Sstevel@tonic-gate goto done; 980Sstevel@tonic-gate } 990Sstevel@tonic-gate if (key->type != pktype) { 1000Sstevel@tonic-gate error("userauth_pubkey: type mismatch for decoded key " 1010Sstevel@tonic-gate "(received %d, expected %d)", key->type, pktype); 1020Sstevel@tonic-gate goto done; 1030Sstevel@tonic-gate } 1040Sstevel@tonic-gate 1050Sstevel@tonic-gate /* Detect and count abandonment */ 1060Sstevel@tonic-gate if (authctxt->method->method_data) { 1070Sstevel@tonic-gate Key *prev_key; 1080Sstevel@tonic-gate unsigned char *prev_pkblob; 1090Sstevel@tonic-gate int prev_blen; 1100Sstevel@tonic-gate 1110Sstevel@tonic-gate /* 1120Sstevel@tonic-gate * Check for earlier test of a key that was allowed but 1130Sstevel@tonic-gate * not followed up with a pubkey req for the same pubkey 1140Sstevel@tonic-gate * and with a signature. 1150Sstevel@tonic-gate */ 1160Sstevel@tonic-gate prev_key = authctxt->method->method_data; 1170Sstevel@tonic-gate if ((prev_blen = key_to_blob(prev_key, 1180Sstevel@tonic-gate &prev_pkblob, NULL))) { 1190Sstevel@tonic-gate if (prev_blen != blen || 1200Sstevel@tonic-gate memcmp(prev_pkblob, pkblob, blen) != 0) { 1210Sstevel@tonic-gate authctxt->method->abandons++; 1220Sstevel@tonic-gate authctxt->method->attempts++; 1230Sstevel@tonic-gate } 1240Sstevel@tonic-gate } 1250Sstevel@tonic-gate key_free(prev_key); 1260Sstevel@tonic-gate authctxt->method->method_data = NULL; 1270Sstevel@tonic-gate } 1280Sstevel@tonic-gate 1290Sstevel@tonic-gate if (have_sig) { 1300Sstevel@tonic-gate sig = packet_get_string(&slen); 1310Sstevel@tonic-gate packet_check_eom(); 1320Sstevel@tonic-gate buffer_init(&b); 1330Sstevel@tonic-gate if (datafellows & SSH_OLD_SESSIONID) { 1340Sstevel@tonic-gate buffer_append(&b, session_id2, session_id2_len); 1350Sstevel@tonic-gate } else { 1360Sstevel@tonic-gate buffer_put_string(&b, session_id2, session_id2_len); 1370Sstevel@tonic-gate } 1380Sstevel@tonic-gate /* reconstruct packet */ 1390Sstevel@tonic-gate buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); 1400Sstevel@tonic-gate buffer_put_cstring(&b, authctxt->user); 1410Sstevel@tonic-gate buffer_put_cstring(&b, 1420Sstevel@tonic-gate datafellows & SSH_BUG_PKSERVICE ? 1430Sstevel@tonic-gate "ssh-userauth" : 1440Sstevel@tonic-gate authctxt->service); 1450Sstevel@tonic-gate if (datafellows & SSH_BUG_PKAUTH) { 1460Sstevel@tonic-gate buffer_put_char(&b, have_sig); 1470Sstevel@tonic-gate } else { 1480Sstevel@tonic-gate buffer_put_cstring(&b, "publickey"); 1490Sstevel@tonic-gate buffer_put_char(&b, have_sig); 1500Sstevel@tonic-gate buffer_put_cstring(&b, pkalg); 1510Sstevel@tonic-gate } 1520Sstevel@tonic-gate buffer_put_string(&b, pkblob, blen); 1530Sstevel@tonic-gate #ifdef DEBUG_PK 1540Sstevel@tonic-gate buffer_dump(&b); 1550Sstevel@tonic-gate #endif 1560Sstevel@tonic-gate /* test for correct signature */ 157*5562Sjp161948 if (user_key_allowed(authctxt->pw, key) && 158*5562Sjp161948 key_verify(key, sig, slen, buffer_ptr(&b), 159*5562Sjp161948 buffer_len(&b)) == 1) { 1600Sstevel@tonic-gate authenticated = 1; 161*5562Sjp161948 } 1620Sstevel@tonic-gate authctxt->method->postponed = 0; 1630Sstevel@tonic-gate buffer_clear(&b); 1640Sstevel@tonic-gate xfree(sig); 1650Sstevel@tonic-gate } else { 1660Sstevel@tonic-gate debug("test whether pkalg/pkblob are acceptable"); 1670Sstevel@tonic-gate packet_check_eom(); 1680Sstevel@tonic-gate 1690Sstevel@tonic-gate /* XXX fake reply and always send PK_OK ? */ 1700Sstevel@tonic-gate /* 1710Sstevel@tonic-gate * XXX this allows testing whether a user is allowed 1720Sstevel@tonic-gate * to login: if you happen to have a valid pubkey this 1730Sstevel@tonic-gate * message is sent. the message is NEVER sent at all 1740Sstevel@tonic-gate * if a user is not allowed to login. is this an 1750Sstevel@tonic-gate * issue? -markus 1760Sstevel@tonic-gate */ 177*5562Sjp161948 if (user_key_allowed(authctxt->pw, key)) { 1780Sstevel@tonic-gate packet_start(SSH2_MSG_USERAUTH_PK_OK); 1790Sstevel@tonic-gate packet_put_string(pkalg, alen); 1800Sstevel@tonic-gate packet_put_string(pkblob, blen); 1810Sstevel@tonic-gate packet_send(); 1820Sstevel@tonic-gate packet_write_wait(); 1830Sstevel@tonic-gate authctxt->method->postponed = 1; 1840Sstevel@tonic-gate /* 1850Sstevel@tonic-gate * Remember key that was tried so we can 1860Sstevel@tonic-gate * correctly detect abandonment. See above. 1870Sstevel@tonic-gate */ 1880Sstevel@tonic-gate authctxt->method->method_data = (void *) key; 1890Sstevel@tonic-gate key = NULL; 1900Sstevel@tonic-gate } 1910Sstevel@tonic-gate } 1920Sstevel@tonic-gate if (authenticated != 1) 1930Sstevel@tonic-gate auth_clear_options(); 1940Sstevel@tonic-gate 1950Sstevel@tonic-gate done: 1960Sstevel@tonic-gate /* 1970Sstevel@tonic-gate * XXX TODO: add config options for specifying users for whom 1980Sstevel@tonic-gate * this userauth is insufficient and what userauths may 1990Sstevel@tonic-gate * continue. 2000Sstevel@tonic-gate */ 2010Sstevel@tonic-gate #ifdef USE_PAM 2020Sstevel@tonic-gate if (authenticated) { 2030Sstevel@tonic-gate if (!do_pam_non_initial_userauth(authctxt)) 2040Sstevel@tonic-gate authenticated = 0; 2050Sstevel@tonic-gate } 2060Sstevel@tonic-gate #endif /* USE_PAM */ 2070Sstevel@tonic-gate 2080Sstevel@tonic-gate debug2("userauth_pubkey: authenticated %d pkalg %s", authenticated, pkalg); 2090Sstevel@tonic-gate if (key != NULL) 2100Sstevel@tonic-gate key_free(key); 2110Sstevel@tonic-gate xfree(pkalg); 2120Sstevel@tonic-gate xfree(pkblob); 2130Sstevel@tonic-gate #ifdef HAVE_CYGWIN 2140Sstevel@tonic-gate if (check_nt_auth(0, authctxt->pw) == 0) 2150Sstevel@tonic-gate return; 2160Sstevel@tonic-gate #endif 2170Sstevel@tonic-gate if (authenticated) 2180Sstevel@tonic-gate authctxt->method->authenticated = 1; 2190Sstevel@tonic-gate } 2200Sstevel@tonic-gate 2210Sstevel@tonic-gate /* return 1 if user allows given key */ 2220Sstevel@tonic-gate static int 2230Sstevel@tonic-gate user_key_allowed2(struct passwd *pw, Key *key, char *file) 2240Sstevel@tonic-gate { 2250Sstevel@tonic-gate char line[8192]; 2260Sstevel@tonic-gate int found_key = 0; 2270Sstevel@tonic-gate FILE *f; 2280Sstevel@tonic-gate u_long linenum = 0; 2290Sstevel@tonic-gate struct stat st; 2300Sstevel@tonic-gate Key *found; 2310Sstevel@tonic-gate char *fp; 2320Sstevel@tonic-gate 2330Sstevel@tonic-gate if (pw == NULL) 2340Sstevel@tonic-gate return 0; 2350Sstevel@tonic-gate 2360Sstevel@tonic-gate /* Temporarily use the user's uid. */ 2370Sstevel@tonic-gate temporarily_use_uid(pw); 2380Sstevel@tonic-gate 2390Sstevel@tonic-gate debug("trying public key file %s", file); 2400Sstevel@tonic-gate 2410Sstevel@tonic-gate /* Fail quietly if file does not exist */ 2420Sstevel@tonic-gate if (stat(file, &st) < 0) { 2430Sstevel@tonic-gate /* Restore the privileged uid. */ 2440Sstevel@tonic-gate restore_uid(); 2450Sstevel@tonic-gate return 0; 2460Sstevel@tonic-gate } 2470Sstevel@tonic-gate /* Open the file containing the authorized keys. */ 2480Sstevel@tonic-gate f = fopen(file, "r"); 2490Sstevel@tonic-gate if (!f) { 2500Sstevel@tonic-gate /* Restore the privileged uid. */ 2510Sstevel@tonic-gate restore_uid(); 2520Sstevel@tonic-gate return 0; 2530Sstevel@tonic-gate } 2540Sstevel@tonic-gate if (options.strict_modes && 2550Sstevel@tonic-gate secure_filename(f, file, pw, line, sizeof(line)) != 0) { 2560Sstevel@tonic-gate (void) fclose(f); 2570Sstevel@tonic-gate log("Authentication refused: %s", line); 2580Sstevel@tonic-gate restore_uid(); 2590Sstevel@tonic-gate return 0; 2600Sstevel@tonic-gate } 2610Sstevel@tonic-gate 2620Sstevel@tonic-gate found_key = 0; 2630Sstevel@tonic-gate found = key_new(key->type); 2640Sstevel@tonic-gate 2650Sstevel@tonic-gate while (fgets(line, sizeof(line), f)) { 2660Sstevel@tonic-gate char *cp, *options = NULL; 2670Sstevel@tonic-gate linenum++; 2680Sstevel@tonic-gate /* Skip leading whitespace, empty and comment lines. */ 2690Sstevel@tonic-gate for (cp = line; *cp == ' ' || *cp == '\t'; cp++) 2700Sstevel@tonic-gate ; 2710Sstevel@tonic-gate if (!*cp || *cp == '\n' || *cp == '#') 2720Sstevel@tonic-gate continue; 2730Sstevel@tonic-gate 2740Sstevel@tonic-gate if (key_read(found, &cp) != 1) { 2750Sstevel@tonic-gate /* no key? check if there are options for this key */ 2760Sstevel@tonic-gate int quoted = 0; 2770Sstevel@tonic-gate debug2("user_key_allowed: check options: '%s'", cp); 2780Sstevel@tonic-gate options = cp; 2790Sstevel@tonic-gate for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { 2800Sstevel@tonic-gate if (*cp == '\\' && cp[1] == '"') 2810Sstevel@tonic-gate cp++; /* Skip both */ 2820Sstevel@tonic-gate else if (*cp == '"') 2830Sstevel@tonic-gate quoted = !quoted; 2840Sstevel@tonic-gate } 2850Sstevel@tonic-gate /* Skip remaining whitespace. */ 2860Sstevel@tonic-gate for (; *cp == ' ' || *cp == '\t'; cp++) 2870Sstevel@tonic-gate ; 2880Sstevel@tonic-gate if (key_read(found, &cp) != 1) { 2890Sstevel@tonic-gate debug2("user_key_allowed: advance: '%s'", cp); 2900Sstevel@tonic-gate /* still no key? advance to next line*/ 2910Sstevel@tonic-gate continue; 2920Sstevel@tonic-gate } 2930Sstevel@tonic-gate } 2940Sstevel@tonic-gate if (key_equal(found, key) && 2950Sstevel@tonic-gate auth_parse_options(pw, options, file, linenum) == 1) { 2960Sstevel@tonic-gate found_key = 1; 2970Sstevel@tonic-gate debug("matching key found: file %s, line %lu", 2980Sstevel@tonic-gate file, linenum); 2990Sstevel@tonic-gate fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX); 3000Sstevel@tonic-gate verbose("Found matching %s key: %s", 3010Sstevel@tonic-gate key_type(found), fp); 3020Sstevel@tonic-gate xfree(fp); 3030Sstevel@tonic-gate break; 3040Sstevel@tonic-gate } 3050Sstevel@tonic-gate } 3060Sstevel@tonic-gate restore_uid(); 3070Sstevel@tonic-gate (void) fclose(f); 3080Sstevel@tonic-gate key_free(found); 3090Sstevel@tonic-gate if (!found_key) 3100Sstevel@tonic-gate debug2("key not found"); 3110Sstevel@tonic-gate return found_key; 3120Sstevel@tonic-gate } 3130Sstevel@tonic-gate 3140Sstevel@tonic-gate /* check whether given key is in .ssh/authorized_keys* */ 3150Sstevel@tonic-gate int 3160Sstevel@tonic-gate user_key_allowed(struct passwd *pw, Key *key) 3170Sstevel@tonic-gate { 3180Sstevel@tonic-gate int success; 3190Sstevel@tonic-gate char *file; 3200Sstevel@tonic-gate 3210Sstevel@tonic-gate if (pw == NULL) 3220Sstevel@tonic-gate return 0; 3230Sstevel@tonic-gate 3240Sstevel@tonic-gate file = authorized_keys_file(pw); 3250Sstevel@tonic-gate success = user_key_allowed2(pw, key, file); 3260Sstevel@tonic-gate xfree(file); 3270Sstevel@tonic-gate if (success) 3280Sstevel@tonic-gate return success; 3290Sstevel@tonic-gate 3300Sstevel@tonic-gate /* try suffix "2" for backward compat, too */ 3310Sstevel@tonic-gate file = authorized_keys_file2(pw); 3320Sstevel@tonic-gate success = user_key_allowed2(pw, key, file); 3330Sstevel@tonic-gate xfree(file); 3340Sstevel@tonic-gate return success; 3350Sstevel@tonic-gate } 3360Sstevel@tonic-gate 3370Sstevel@tonic-gate static 3380Sstevel@tonic-gate void 3390Sstevel@tonic-gate userauth_pubkey_abandon(Authctxt *authctxt, Authmethod *method) 3400Sstevel@tonic-gate { 3410Sstevel@tonic-gate if (!authctxt || !method) 3420Sstevel@tonic-gate return; 3430Sstevel@tonic-gate 3440Sstevel@tonic-gate if (method->method_data) { 3450Sstevel@tonic-gate method->abandons++; 3460Sstevel@tonic-gate method->attempts++; 3470Sstevel@tonic-gate key_free((Key *) method->method_data); 3480Sstevel@tonic-gate method->method_data = NULL; 3490Sstevel@tonic-gate } 3500Sstevel@tonic-gate } 3510Sstevel@tonic-gate 3520Sstevel@tonic-gate Authmethod method_pubkey = { 3530Sstevel@tonic-gate "publickey", 3540Sstevel@tonic-gate &options.pubkey_authentication, 3550Sstevel@tonic-gate userauth_pubkey, 3560Sstevel@tonic-gate userauth_pubkey_abandon, 3570Sstevel@tonic-gate NULL, NULL, /* method data and hist data */ 3580Sstevel@tonic-gate 0, /* not initial userauth */ 3590Sstevel@tonic-gate 0, 0, 0, /* counters */ 3600Sstevel@tonic-gate 0, 0, 0, 0, 0, 0 /* state */ 3610Sstevel@tonic-gate }; 362