1 /* $OpenBSD: auth2-pubkey.c,v 1.19 2008/07/03 21:46:58 otto Exp $ */ 2 /* 3 * Copyright (c) 2000 Markus Friedl. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 27 #include <sys/types.h> 28 #include <sys/stat.h> 29 30 #include <fcntl.h> 31 #include <pwd.h> 32 #include <stdio.h> 33 #include <stdarg.h> 34 #include <unistd.h> 35 36 #include "xmalloc.h" 37 #include "ssh.h" 38 #include "ssh2.h" 39 #include "packet.h" 40 #include "buffer.h" 41 #include "log.h" 42 #include "servconf.h" 43 #include "compat.h" 44 #include "key.h" 45 #include "hostfile.h" 46 #include "auth.h" 47 #include "pathnames.h" 48 #include "uidswap.h" 49 #include "auth-options.h" 50 #include "canohost.h" 51 #ifdef GSSAPI 52 #include "ssh-gss.h" 53 #endif 54 #include "monitor_wrap.h" 55 #include "misc.h" 56 57 /* import */ 58 extern ServerOptions options; 59 extern u_char *session_id2; 60 extern u_int session_id2_len; 61 62 static int 63 userauth_pubkey(Authctxt *authctxt) 64 { 65 Buffer b; 66 Key *key = NULL; 67 char *pkalg; 68 u_char *pkblob, *sig; 69 u_int alen, blen, slen; 70 int have_sig, pktype; 71 int authenticated = 0; 72 73 if (!authctxt->valid) { 74 debug2("userauth_pubkey: disabled because of invalid user"); 75 return 0; 76 } 77 have_sig = packet_get_char(); 78 if (datafellows & SSH_BUG_PKAUTH) { 79 debug2("userauth_pubkey: SSH_BUG_PKAUTH"); 80 /* no explicit pkalg given */ 81 pkblob = packet_get_string(&blen); 82 buffer_init(&b); 83 buffer_append(&b, pkblob, blen); 84 /* so we have to extract the pkalg from the pkblob */ 85 pkalg = buffer_get_string(&b, &alen); 86 buffer_free(&b); 87 } else { 88 pkalg = packet_get_string(&alen); 89 pkblob = packet_get_string(&blen); 90 } 91 pktype = key_type_from_name(pkalg); 92 if (pktype == KEY_UNSPEC) { 93 /* this is perfectly legal */ 94 logit("userauth_pubkey: unsupported public key algorithm: %s", 95 pkalg); 96 goto done; 97 } 98 key = key_from_blob(pkblob, blen); 99 if (key == NULL) { 100 error("userauth_pubkey: cannot decode key: %s", pkalg); 101 goto done; 102 } 103 if (key->type != pktype) { 104 error("userauth_pubkey: type mismatch for decoded key " 105 "(received %d, expected %d)", key->type, pktype); 106 goto done; 107 } 108 if (have_sig) { 109 sig = packet_get_string(&slen); 110 packet_check_eom(); 111 buffer_init(&b); 112 if (datafellows & SSH_OLD_SESSIONID) { 113 buffer_append(&b, session_id2, session_id2_len); 114 } else { 115 buffer_put_string(&b, session_id2, session_id2_len); 116 } 117 /* reconstruct packet */ 118 buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); 119 buffer_put_cstring(&b, authctxt->user); 120 buffer_put_cstring(&b, 121 datafellows & SSH_BUG_PKSERVICE ? 122 "ssh-userauth" : 123 authctxt->service); 124 if (datafellows & SSH_BUG_PKAUTH) { 125 buffer_put_char(&b, have_sig); 126 } else { 127 buffer_put_cstring(&b, "publickey"); 128 buffer_put_char(&b, have_sig); 129 buffer_put_cstring(&b, pkalg); 130 } 131 buffer_put_string(&b, pkblob, blen); 132 #ifdef DEBUG_PK 133 buffer_dump(&b); 134 #endif 135 /* test for correct signature */ 136 authenticated = 0; 137 if (PRIVSEP(user_key_allowed(authctxt->pw, key)) && 138 PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b), 139 buffer_len(&b))) == 1) 140 authenticated = 1; 141 buffer_free(&b); 142 xfree(sig); 143 } else { 144 debug("test whether pkalg/pkblob are acceptable"); 145 packet_check_eom(); 146 147 /* XXX fake reply and always send PK_OK ? */ 148 /* 149 * XXX this allows testing whether a user is allowed 150 * to login: if you happen to have a valid pubkey this 151 * message is sent. the message is NEVER sent at all 152 * if a user is not allowed to login. is this an 153 * issue? -markus 154 */ 155 if (PRIVSEP(user_key_allowed(authctxt->pw, key))) { 156 packet_start(SSH2_MSG_USERAUTH_PK_OK); 157 packet_put_string(pkalg, alen); 158 packet_put_string(pkblob, blen); 159 packet_send(); 160 packet_write_wait(); 161 authctxt->postponed = 1; 162 } 163 } 164 if (authenticated != 1) 165 auth_clear_options(); 166 done: 167 debug2("userauth_pubkey: authenticated %d pkalg %s", authenticated, pkalg); 168 if (key != NULL) 169 key_free(key); 170 xfree(pkalg); 171 xfree(pkblob); 172 return authenticated; 173 } 174 175 /* return 1 if user allows given key */ 176 static int 177 user_key_allowed2(struct passwd *pw, Key *key, char *file) 178 { 179 char line[SSH_MAX_PUBKEY_BYTES]; 180 int found_key = 0; 181 FILE *f; 182 u_long linenum = 0; 183 Key *found; 184 char *fp; 185 186 /* Temporarily use the user's uid. */ 187 temporarily_use_uid(pw); 188 189 debug("trying public key file %s", file); 190 f = auth_openkeyfile(file, pw, options.strict_modes); 191 192 if (!f) { 193 restore_uid(); 194 return 0; 195 } 196 197 found_key = 0; 198 found = key_new(key->type); 199 200 while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) { 201 char *cp, *key_options = NULL; 202 203 /* Skip leading whitespace, empty and comment lines. */ 204 for (cp = line; *cp == ' ' || *cp == '\t'; cp++) 205 ; 206 if (!*cp || *cp == '\n' || *cp == '#') 207 continue; 208 209 if (key_read(found, &cp) != 1) { 210 /* no key? check if there are options for this key */ 211 int quoted = 0; 212 debug2("user_key_allowed: check options: '%s'", cp); 213 key_options = cp; 214 for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { 215 if (*cp == '\\' && cp[1] == '"') 216 cp++; /* Skip both */ 217 else if (*cp == '"') 218 quoted = !quoted; 219 } 220 /* Skip remaining whitespace. */ 221 for (; *cp == ' ' || *cp == '\t'; cp++) 222 ; 223 if (key_read(found, &cp) != 1) { 224 debug2("user_key_allowed: advance: '%s'", cp); 225 /* still no key? advance to next line*/ 226 continue; 227 } 228 } 229 if (key_equal(found, key) && 230 auth_parse_options(pw, key_options, file, linenum) == 1) { 231 found_key = 1; 232 debug("matching key found: file %s, line %lu", 233 file, linenum); 234 fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX); 235 verbose("Found matching %s key: %s", 236 key_type(found), fp); 237 xfree(fp); 238 break; 239 } 240 } 241 restore_uid(); 242 fclose(f); 243 key_free(found); 244 if (!found_key) 245 debug2("key not found"); 246 return found_key; 247 } 248 249 /* check whether given key is in .ssh/authorized_keys* */ 250 int 251 user_key_allowed(struct passwd *pw, Key *key) 252 { 253 int success; 254 char *file; 255 256 file = authorized_keys_file(pw); 257 success = user_key_allowed2(pw, key, file); 258 xfree(file); 259 if (success) 260 return success; 261 262 /* try suffix "2" for backward compat, too */ 263 file = authorized_keys_file2(pw); 264 success = user_key_allowed2(pw, key, file); 265 xfree(file); 266 return success; 267 } 268 269 Authmethod method_pubkey = { 270 "publickey", 271 userauth_pubkey, 272 &options.pubkey_authentication 273 }; 274