1323136Sdes/* $OpenBSD: auth2-pubkey.c,v 1.62 2017/01/30 01:03:00 djm Exp $ */ 298675Sdes/* 398675Sdes * Copyright (c) 2000 Markus Friedl. All rights reserved. 498675Sdes * 598675Sdes * Redistribution and use in source and binary forms, with or without 698675Sdes * modification, are permitted provided that the following conditions 798675Sdes * are met: 898675Sdes * 1. Redistributions of source code must retain the above copyright 998675Sdes * notice, this list of conditions and the following disclaimer. 1098675Sdes * 2. Redistributions in binary form must reproduce the above copyright 1198675Sdes * notice, this list of conditions and the following disclaimer in the 1298675Sdes * documentation and/or other materials provided with the distribution. 1398675Sdes * 1498675Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1598675Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1698675Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1798675Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1898675Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 1998675Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2098675Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2198675Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2298675Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2398675Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2498675Sdes */ 2598675Sdes 2698675Sdes#include "includes.h" 2798675Sdes 28162852Sdes#include <sys/types.h> 29162852Sdes#include <sys/stat.h> 30248619Sdes#include <sys/wait.h> 31162852Sdes 32248619Sdes#include <errno.h> 33181111Sdes#include <fcntl.h> 34248619Sdes#ifdef HAVE_PATHS_H 35248619Sdes# include <paths.h> 36248619Sdes#endif 37162852Sdes#include <pwd.h> 38248619Sdes#include <signal.h> 39162852Sdes#include <stdio.h> 40162852Sdes#include <stdarg.h> 41204917Sdes#include <string.h> 42204917Sdes#include <time.h> 43181111Sdes#include <unistd.h> 44294332Sdes#include <limits.h> 45162852Sdes 46162852Sdes#include "xmalloc.h" 47146998Sdes#include "ssh.h" 4898675Sdes#include "ssh2.h" 4998675Sdes#include "packet.h" 5098675Sdes#include "buffer.h" 5198675Sdes#include "log.h" 52294328Sdes#include "misc.h" 5398675Sdes#include "servconf.h" 5498675Sdes#include "compat.h" 55162852Sdes#include "key.h" 56162852Sdes#include "hostfile.h" 5798675Sdes#include "auth.h" 5898675Sdes#include "pathnames.h" 5998675Sdes#include "uidswap.h" 6098675Sdes#include "auth-options.h" 6198675Sdes#include "canohost.h" 62162852Sdes#ifdef GSSAPI 63162852Sdes#include "ssh-gss.h" 64162852Sdes#endif 6598675Sdes#include "monitor_wrap.h" 66204917Sdes#include "authfile.h" 67215116Sdes#include "match.h" 68294336Sdes#include "ssherr.h" 69294336Sdes#include "channels.h" /* XXX for session.h */ 70294336Sdes#include "session.h" /* XXX for child_set_env(); refactor? */ 7198675Sdes 7298675Sdes/* import */ 7398675Sdesextern ServerOptions options; 7498675Sdesextern u_char *session_id2; 75124208Sdesextern u_int session_id2_len; 7698675Sdes 7798675Sdesstatic int 7898675Sdesuserauth_pubkey(Authctxt *authctxt) 7998675Sdes{ 8098675Sdes Buffer b; 8198675Sdes Key *key = NULL; 82296633Sdes char *pkalg, *userstyle, *fp = NULL; 8398675Sdes u_char *pkblob, *sig; 8498675Sdes u_int alen, blen, slen; 8598675Sdes int have_sig, pktype; 8698675Sdes int authenticated = 0; 8798675Sdes 8898675Sdes if (!authctxt->valid) { 89296633Sdes debug2("%s: disabled because of invalid user", __func__); 9098675Sdes return 0; 9198675Sdes } 9298675Sdes have_sig = packet_get_char(); 9398675Sdes if (datafellows & SSH_BUG_PKAUTH) { 94296633Sdes debug2("%s: SSH_BUG_PKAUTH", __func__); 9598675Sdes /* no explicit pkalg given */ 9698675Sdes pkblob = packet_get_string(&blen); 9798675Sdes buffer_init(&b); 9898675Sdes buffer_append(&b, pkblob, blen); 9998675Sdes /* so we have to extract the pkalg from the pkblob */ 10098675Sdes pkalg = buffer_get_string(&b, &alen); 10198675Sdes buffer_free(&b); 10298675Sdes } else { 10398675Sdes pkalg = packet_get_string(&alen); 10498675Sdes pkblob = packet_get_string(&blen); 10598675Sdes } 10698675Sdes pktype = key_type_from_name(pkalg); 10798675Sdes if (pktype == KEY_UNSPEC) { 10898675Sdes /* this is perfectly legal */ 109296633Sdes logit("%s: unsupported public key algorithm: %s", 110296633Sdes __func__, pkalg); 11198675Sdes goto done; 11298675Sdes } 11398675Sdes key = key_from_blob(pkblob, blen); 11498675Sdes if (key == NULL) { 115296633Sdes error("%s: cannot decode key: %s", __func__, pkalg); 11698675Sdes goto done; 11798675Sdes } 11898675Sdes if (key->type != pktype) { 119296633Sdes error("%s: type mismatch for decoded key " 120296633Sdes "(received %d, expected %d)", __func__, key->type, pktype); 12198675Sdes goto done; 12298675Sdes } 123261320Sdes if (key_type_plain(key->type) == KEY_RSA && 124261320Sdes (datafellows & SSH_BUG_RSASIGMD5) != 0) { 125261320Sdes logit("Refusing RSA key because client uses unsafe " 126261320Sdes "signature scheme"); 127261320Sdes goto done; 128261320Sdes } 129296633Sdes fp = sshkey_fingerprint(key, options.fingerprint_hash, SSH_FP_DEFAULT); 130294332Sdes if (auth2_userkey_already_used(authctxt, key)) { 131294332Sdes logit("refusing previously-used %s key", key_type(key)); 132294332Sdes goto done; 133294332Sdes } 134294336Sdes if (match_pattern_list(sshkey_ssh_name(key), 135294336Sdes options.pubkey_key_types, 0) != 1) { 136294332Sdes logit("%s: key type %s not in PubkeyAcceptedKeyTypes", 137294332Sdes __func__, sshkey_ssh_name(key)); 138294332Sdes goto done; 139294332Sdes } 140294332Sdes 14198675Sdes if (have_sig) { 142296633Sdes debug3("%s: have signature for %s %s", 143296633Sdes __func__, sshkey_type(key), fp); 14498675Sdes sig = packet_get_string(&slen); 14598675Sdes packet_check_eom(); 14698675Sdes buffer_init(&b); 14798675Sdes if (datafellows & SSH_OLD_SESSIONID) { 14898675Sdes buffer_append(&b, session_id2, session_id2_len); 14998675Sdes } else { 15098675Sdes buffer_put_string(&b, session_id2, session_id2_len); 15198675Sdes } 15298675Sdes /* reconstruct packet */ 15398675Sdes buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); 154255767Sdes xasprintf(&userstyle, "%s%s%s", authctxt->user, 155255767Sdes authctxt->style ? ":" : "", 156255767Sdes authctxt->style ? authctxt->style : ""); 157255767Sdes buffer_put_cstring(&b, userstyle); 158255767Sdes free(userstyle); 15998675Sdes buffer_put_cstring(&b, 16098675Sdes datafellows & SSH_BUG_PKSERVICE ? 16198675Sdes "ssh-userauth" : 16298675Sdes authctxt->service); 16398675Sdes if (datafellows & SSH_BUG_PKAUTH) { 16498675Sdes buffer_put_char(&b, have_sig); 16598675Sdes } else { 16698675Sdes buffer_put_cstring(&b, "publickey"); 16798675Sdes buffer_put_char(&b, have_sig); 16898675Sdes buffer_put_cstring(&b, pkalg); 16998675Sdes } 17098675Sdes buffer_put_string(&b, pkblob, blen); 17198675Sdes#ifdef DEBUG_PK 17298675Sdes buffer_dump(&b); 17398675Sdes#endif 174255767Sdes pubkey_auth_info(authctxt, key, NULL); 175255767Sdes 17698675Sdes /* test for correct signature */ 17798675Sdes authenticated = 0; 178294336Sdes if (PRIVSEP(user_key_allowed(authctxt->pw, key, 1)) && 17998675Sdes PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b), 180294332Sdes buffer_len(&b))) == 1) { 18198675Sdes authenticated = 1; 182294332Sdes /* Record the successful key to prevent reuse */ 183294332Sdes auth2_record_userkey(authctxt, key); 184294332Sdes key = NULL; /* Don't free below */ 185294332Sdes } 186126274Sdes buffer_free(&b); 187255767Sdes free(sig); 18898675Sdes } else { 189296633Sdes debug("%s: test whether pkalg/pkblob are acceptable for %s %s", 190296633Sdes __func__, sshkey_type(key), fp); 19198675Sdes packet_check_eom(); 19298675Sdes 19398675Sdes /* XXX fake reply and always send PK_OK ? */ 19498675Sdes /* 19598675Sdes * XXX this allows testing whether a user is allowed 19698675Sdes * to login: if you happen to have a valid pubkey this 19798675Sdes * message is sent. the message is NEVER sent at all 19898675Sdes * if a user is not allowed to login. is this an 19998675Sdes * issue? -markus 20098675Sdes */ 201294336Sdes if (PRIVSEP(user_key_allowed(authctxt->pw, key, 0))) { 20298675Sdes packet_start(SSH2_MSG_USERAUTH_PK_OK); 20398675Sdes packet_put_string(pkalg, alen); 20498675Sdes packet_put_string(pkblob, blen); 20598675Sdes packet_send(); 20698675Sdes packet_write_wait(); 20798675Sdes authctxt->postponed = 1; 20898675Sdes } 20998675Sdes } 21098675Sdes if (authenticated != 1) 21198675Sdes auth_clear_options(); 21298675Sdesdone: 213296633Sdes debug2("%s: authenticated %d pkalg %s", __func__, authenticated, pkalg); 21498675Sdes if (key != NULL) 21598675Sdes key_free(key); 216255767Sdes free(pkalg); 217255767Sdes free(pkblob); 218296633Sdes free(fp); 21998675Sdes return authenticated; 22098675Sdes} 22198675Sdes 222255767Sdesvoid 223255767Sdespubkey_auth_info(Authctxt *authctxt, const Key *key, const char *fmt, ...) 224255767Sdes{ 225255767Sdes char *fp, *extra; 226255767Sdes va_list ap; 227255767Sdes int i; 228255767Sdes 229255767Sdes extra = NULL; 230255767Sdes if (fmt != NULL) { 231255767Sdes va_start(ap, fmt); 232255767Sdes i = vasprintf(&extra, fmt, ap); 233255767Sdes va_end(ap); 234255767Sdes if (i < 0 || extra == NULL) 235255767Sdes fatal("%s: vasprintf failed", __func__); 236255767Sdes } 237255767Sdes 238255767Sdes if (key_is_cert(key)) { 239294332Sdes fp = sshkey_fingerprint(key->cert->signature_key, 240294332Sdes options.fingerprint_hash, SSH_FP_DEFAULT); 241255767Sdes auth_info(authctxt, "%s ID %s (serial %llu) CA %s %s%s%s", 242255767Sdes key_type(key), key->cert->key_id, 243255767Sdes (unsigned long long)key->cert->serial, 244294332Sdes key_type(key->cert->signature_key), 245294332Sdes fp == NULL ? "(null)" : fp, 246255767Sdes extra == NULL ? "" : ", ", extra == NULL ? "" : extra); 247255767Sdes free(fp); 248255767Sdes } else { 249294332Sdes fp = sshkey_fingerprint(key, options.fingerprint_hash, 250294332Sdes SSH_FP_DEFAULT); 251294332Sdes auth_info(authctxt, "%s %s%s%s", key_type(key), 252294332Sdes fp == NULL ? "(null)" : fp, 253255767Sdes extra == NULL ? "" : ", ", extra == NULL ? "" : extra); 254255767Sdes free(fp); 255255767Sdes } 256255767Sdes free(extra); 257255767Sdes} 258255767Sdes 259294336Sdes/* 260294336Sdes * Splits 's' into an argument vector. Handles quoted string and basic 261294336Sdes * escape characters (\\, \", \'). Caller must free the argument vector 262294336Sdes * and its members. 263294336Sdes */ 264215116Sdesstatic int 265294336Sdessplit_argv(const char *s, int *argcp, char ***argvp) 266294336Sdes{ 267294336Sdes int r = SSH_ERR_INTERNAL_ERROR; 268294336Sdes int argc = 0, quote, i, j; 269294336Sdes char *arg, **argv = xcalloc(1, sizeof(*argv)); 270294336Sdes 271294336Sdes *argvp = NULL; 272294336Sdes *argcp = 0; 273294336Sdes 274294336Sdes for (i = 0; s[i] != '\0'; i++) { 275294336Sdes /* Skip leading whitespace */ 276294336Sdes if (s[i] == ' ' || s[i] == '\t') 277294336Sdes continue; 278294336Sdes 279294336Sdes /* Start of a token */ 280294336Sdes quote = 0; 281294336Sdes if (s[i] == '\\' && 282294336Sdes (s[i + 1] == '\'' || s[i + 1] == '\"' || s[i + 1] == '\\')) 283294336Sdes i++; 284294336Sdes else if (s[i] == '\'' || s[i] == '"') 285294336Sdes quote = s[i++]; 286294336Sdes 287294336Sdes argv = xreallocarray(argv, (argc + 2), sizeof(*argv)); 288294336Sdes arg = argv[argc++] = xcalloc(1, strlen(s + i) + 1); 289294336Sdes argv[argc] = NULL; 290294336Sdes 291294336Sdes /* Copy the token in, removing escapes */ 292294336Sdes for (j = 0; s[i] != '\0'; i++) { 293294336Sdes if (s[i] == '\\') { 294294336Sdes if (s[i + 1] == '\'' || 295294336Sdes s[i + 1] == '\"' || 296294336Sdes s[i + 1] == '\\') { 297294336Sdes i++; /* Skip '\' */ 298294336Sdes arg[j++] = s[i]; 299294336Sdes } else { 300294336Sdes /* Unrecognised escape */ 301294336Sdes arg[j++] = s[i]; 302294336Sdes } 303294336Sdes } else if (quote == 0 && (s[i] == ' ' || s[i] == '\t')) 304294336Sdes break; /* done */ 305294336Sdes else if (quote != 0 && s[i] == quote) 306294336Sdes break; /* done */ 307294336Sdes else 308294336Sdes arg[j++] = s[i]; 309294336Sdes } 310294336Sdes if (s[i] == '\0') { 311294336Sdes if (quote != 0) { 312294336Sdes /* Ran out of string looking for close quote */ 313294336Sdes r = SSH_ERR_INVALID_FORMAT; 314294336Sdes goto out; 315294336Sdes } 316294336Sdes break; 317294336Sdes } 318294336Sdes } 319294336Sdes /* Success */ 320294336Sdes *argcp = argc; 321294336Sdes *argvp = argv; 322294336Sdes argc = 0; 323294336Sdes argv = NULL; 324294336Sdes r = 0; 325294336Sdes out: 326294336Sdes if (argc != 0 && argv != NULL) { 327294336Sdes for (i = 0; i < argc; i++) 328294336Sdes free(argv[i]); 329294336Sdes free(argv); 330294336Sdes } 331294336Sdes return r; 332294336Sdes} 333294336Sdes 334294336Sdes/* 335294336Sdes * Reassemble an argument vector into a string, quoting and escaping as 336294336Sdes * necessary. Caller must free returned string. 337294336Sdes */ 338294336Sdesstatic char * 339294336Sdesassemble_argv(int argc, char **argv) 340294336Sdes{ 341294336Sdes int i, j, ws, r; 342294336Sdes char c, *ret; 343294336Sdes struct sshbuf *buf, *arg; 344294336Sdes 345294336Sdes if ((buf = sshbuf_new()) == NULL || (arg = sshbuf_new()) == NULL) 346294336Sdes fatal("%s: sshbuf_new failed", __func__); 347294336Sdes 348294336Sdes for (i = 0; i < argc; i++) { 349294336Sdes ws = 0; 350294336Sdes sshbuf_reset(arg); 351294336Sdes for (j = 0; argv[i][j] != '\0'; j++) { 352294336Sdes r = 0; 353294336Sdes c = argv[i][j]; 354294336Sdes switch (c) { 355294336Sdes case ' ': 356294336Sdes case '\t': 357294336Sdes ws = 1; 358294336Sdes r = sshbuf_put_u8(arg, c); 359294336Sdes break; 360294336Sdes case '\\': 361294336Sdes case '\'': 362294336Sdes case '"': 363294336Sdes if ((r = sshbuf_put_u8(arg, '\\')) != 0) 364294336Sdes break; 365294336Sdes /* FALLTHROUGH */ 366294336Sdes default: 367294336Sdes r = sshbuf_put_u8(arg, c); 368294336Sdes break; 369294336Sdes } 370294336Sdes if (r != 0) 371294336Sdes fatal("%s: sshbuf_put_u8: %s", 372294336Sdes __func__, ssh_err(r)); 373294336Sdes } 374294336Sdes if ((i != 0 && (r = sshbuf_put_u8(buf, ' ')) != 0) || 375294336Sdes (ws != 0 && (r = sshbuf_put_u8(buf, '"')) != 0) || 376294336Sdes (r = sshbuf_putb(buf, arg)) != 0 || 377294336Sdes (ws != 0 && (r = sshbuf_put_u8(buf, '"')) != 0)) 378294336Sdes fatal("%s: buffer error: %s", __func__, ssh_err(r)); 379294336Sdes } 380294336Sdes if ((ret = malloc(sshbuf_len(buf) + 1)) == NULL) 381294336Sdes fatal("%s: malloc failed", __func__); 382294336Sdes memcpy(ret, sshbuf_ptr(buf), sshbuf_len(buf)); 383294336Sdes ret[sshbuf_len(buf)] = '\0'; 384294336Sdes sshbuf_free(buf); 385294336Sdes sshbuf_free(arg); 386294336Sdes return ret; 387294336Sdes} 388294336Sdes 389294336Sdes/* 390294336Sdes * Runs command in a subprocess. Returns pid on success and a FILE* to the 391294336Sdes * subprocess' stdout or 0 on failure. 392294336Sdes * NB. "command" is only used for logging. 393294336Sdes */ 394294336Sdesstatic pid_t 395294336Sdessubprocess(const char *tag, struct passwd *pw, const char *command, 396294336Sdes int ac, char **av, FILE **child) 397294336Sdes{ 398294336Sdes FILE *f; 399294336Sdes struct stat st; 400294336Sdes int devnull, p[2], i; 401294336Sdes pid_t pid; 402294336Sdes char *cp, errmsg[512]; 403294336Sdes u_int envsize; 404294336Sdes char **child_env; 405294336Sdes 406294336Sdes *child = NULL; 407294336Sdes 408294336Sdes debug3("%s: %s command \"%s\" running as %s", __func__, 409294336Sdes tag, command, pw->pw_name); 410294336Sdes 411294336Sdes /* Verify the path exists and is safe-ish to execute */ 412294336Sdes if (*av[0] != '/') { 413294336Sdes error("%s path is not absolute", tag); 414294336Sdes return 0; 415294336Sdes } 416294336Sdes temporarily_use_uid(pw); 417294336Sdes if (stat(av[0], &st) < 0) { 418294336Sdes error("Could not stat %s \"%s\": %s", tag, 419294336Sdes av[0], strerror(errno)); 420294336Sdes restore_uid(); 421294336Sdes return 0; 422294336Sdes } 423294336Sdes if (auth_secure_path(av[0], &st, NULL, 0, 424294336Sdes errmsg, sizeof(errmsg)) != 0) { 425294336Sdes error("Unsafe %s \"%s\": %s", tag, av[0], errmsg); 426294336Sdes restore_uid(); 427294336Sdes return 0; 428294336Sdes } 429294336Sdes 430294336Sdes /* 431294336Sdes * Run the command; stderr is left in place, stdout is the 432294336Sdes * authorized_keys output. 433294336Sdes */ 434294336Sdes if (pipe(p) != 0) { 435294336Sdes error("%s: pipe: %s", tag, strerror(errno)); 436294336Sdes restore_uid(); 437294336Sdes return 0; 438294336Sdes } 439294336Sdes 440294336Sdes /* 441294336Sdes * Don't want to call this in the child, where it can fatal() and 442294336Sdes * run cleanup_exit() code. 443294336Sdes */ 444294336Sdes restore_uid(); 445294336Sdes 446294336Sdes switch ((pid = fork())) { 447294336Sdes case -1: /* error */ 448294336Sdes error("%s: fork: %s", tag, strerror(errno)); 449294336Sdes close(p[0]); 450294336Sdes close(p[1]); 451294336Sdes return 0; 452294336Sdes case 0: /* child */ 453294336Sdes /* Prepare a minimal environment for the child. */ 454294336Sdes envsize = 5; 455294336Sdes child_env = xcalloc(sizeof(*child_env), envsize); 456294336Sdes child_set_env(&child_env, &envsize, "PATH", _PATH_STDPATH); 457294336Sdes child_set_env(&child_env, &envsize, "USER", pw->pw_name); 458294336Sdes child_set_env(&child_env, &envsize, "LOGNAME", pw->pw_name); 459294336Sdes child_set_env(&child_env, &envsize, "HOME", pw->pw_dir); 460294336Sdes if ((cp = getenv("LANG")) != NULL) 461294336Sdes child_set_env(&child_env, &envsize, "LANG", cp); 462294336Sdes 463294336Sdes for (i = 0; i < NSIG; i++) 464294336Sdes signal(i, SIG_DFL); 465294336Sdes 466294336Sdes if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) { 467294336Sdes error("%s: open %s: %s", tag, _PATH_DEVNULL, 468294336Sdes strerror(errno)); 469294336Sdes _exit(1); 470294336Sdes } 471294336Sdes /* Keep stderr around a while longer to catch errors */ 472294336Sdes if (dup2(devnull, STDIN_FILENO) == -1 || 473294336Sdes dup2(p[1], STDOUT_FILENO) == -1) { 474294336Sdes error("%s: dup2: %s", tag, strerror(errno)); 475294336Sdes _exit(1); 476294336Sdes } 477294336Sdes closefrom(STDERR_FILENO + 1); 478294336Sdes 479294336Sdes /* Don't use permanently_set_uid() here to avoid fatal() */ 480294336Sdes if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) { 481294336Sdes error("%s: setresgid %u: %s", tag, (u_int)pw->pw_gid, 482294336Sdes strerror(errno)); 483294336Sdes _exit(1); 484294336Sdes } 485294336Sdes if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0) { 486294336Sdes error("%s: setresuid %u: %s", tag, (u_int)pw->pw_uid, 487294336Sdes strerror(errno)); 488294336Sdes _exit(1); 489294336Sdes } 490294336Sdes /* stdin is pointed to /dev/null at this point */ 491294336Sdes if (dup2(STDIN_FILENO, STDERR_FILENO) == -1) { 492294336Sdes error("%s: dup2: %s", tag, strerror(errno)); 493294336Sdes _exit(1); 494294336Sdes } 495294336Sdes 496294336Sdes execve(av[0], av, child_env); 497294336Sdes error("%s exec \"%s\": %s", tag, command, strerror(errno)); 498294336Sdes _exit(127); 499294336Sdes default: /* parent */ 500294336Sdes break; 501294336Sdes } 502294336Sdes 503294336Sdes close(p[1]); 504294336Sdes if ((f = fdopen(p[0], "r")) == NULL) { 505294336Sdes error("%s: fdopen: %s", tag, strerror(errno)); 506294336Sdes close(p[0]); 507294336Sdes /* Don't leave zombie child */ 508294336Sdes kill(pid, SIGTERM); 509294336Sdes while (waitpid(pid, NULL, 0) == -1 && errno == EINTR) 510294336Sdes ; 511294336Sdes return 0; 512294336Sdes } 513294336Sdes /* Success */ 514294336Sdes debug3("%s: %s pid %ld", __func__, tag, (long)pid); 515294336Sdes *child = f; 516294336Sdes return pid; 517294336Sdes} 518294336Sdes 519294336Sdes/* Returns 0 if pid exited cleanly, non-zero otherwise */ 520294336Sdesstatic int 521294336Sdesexited_cleanly(pid_t pid, const char *tag, const char *cmd) 522294336Sdes{ 523294336Sdes int status; 524294336Sdes 525294336Sdes while (waitpid(pid, &status, 0) == -1) { 526294336Sdes if (errno != EINTR) { 527294336Sdes error("%s: waitpid: %s", tag, strerror(errno)); 528294336Sdes return -1; 529294336Sdes } 530294336Sdes } 531294336Sdes if (WIFSIGNALED(status)) { 532294336Sdes error("%s %s exited on signal %d", tag, cmd, WTERMSIG(status)); 533294336Sdes return -1; 534294336Sdes } else if (WEXITSTATUS(status) != 0) { 535294336Sdes error("%s %s failed, status %d", tag, cmd, WEXITSTATUS(status)); 536294336Sdes return -1; 537294336Sdes } 538294336Sdes return 0; 539294336Sdes} 540294336Sdes 541294336Sdesstatic int 542294328Sdesmatch_principals_option(const char *principal_list, struct sshkey_cert *cert) 543215116Sdes{ 544215116Sdes char *result; 545215116Sdes u_int i; 546215116Sdes 547215116Sdes /* XXX percent_expand() sequences for authorized_principals? */ 548215116Sdes 549215116Sdes for (i = 0; i < cert->nprincipals; i++) { 550215116Sdes if ((result = match_list(cert->principals[i], 551215116Sdes principal_list, NULL)) != NULL) { 552215116Sdes debug3("matched principal from key options \"%.100s\"", 553215116Sdes result); 554255767Sdes free(result); 555215116Sdes return 1; 556215116Sdes } 557215116Sdes } 558215116Sdes return 0; 559215116Sdes} 560215116Sdes 561215116Sdesstatic int 562294336Sdesprocess_principals(FILE *f, char *file, struct passwd *pw, 563323134Sdes const struct sshkey_cert *cert) 564215116Sdes{ 565215116Sdes char line[SSH_MAX_PUBKEY_BYTES], *cp, *ep, *line_opts; 566215116Sdes u_long linenum = 0; 567323136Sdes u_int i, found_principal = 0; 568215116Sdes 569215116Sdes while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) { 570323136Sdes /* Always consume entire input */ 571323136Sdes if (found_principal) 572323136Sdes continue; 573215116Sdes /* Skip leading whitespace. */ 574215116Sdes for (cp = line; *cp == ' ' || *cp == '\t'; cp++) 575215116Sdes ; 576215116Sdes /* Skip blank and comment lines. */ 577215116Sdes if ((ep = strchr(cp, '#')) != NULL) 578215116Sdes *ep = '\0'; 579215116Sdes if (!*cp || *cp == '\n') 580215116Sdes continue; 581215116Sdes /* Trim trailing whitespace. */ 582215116Sdes ep = cp + strlen(cp) - 1; 583215116Sdes while (ep > cp && (*ep == '\n' || *ep == ' ' || *ep == '\t')) 584215116Sdes *ep-- = '\0'; 585215116Sdes /* 586215116Sdes * If the line has internal whitespace then assume it has 587215116Sdes * key options. 588215116Sdes */ 589215116Sdes line_opts = NULL; 590215116Sdes if ((ep = strrchr(cp, ' ')) != NULL || 591215116Sdes (ep = strrchr(cp, '\t')) != NULL) { 592215116Sdes for (; *ep == ' ' || *ep == '\t'; ep++) 593221420Sdes ; 594215116Sdes line_opts = cp; 595215116Sdes cp = ep; 596215116Sdes } 597215116Sdes for (i = 0; i < cert->nprincipals; i++) { 598215116Sdes if (strcmp(cp, cert->principals[i]) == 0) { 599294336Sdes debug3("%s:%lu: matched principal \"%.100s\"", 600294336Sdes file == NULL ? "(command)" : file, 601294336Sdes linenum, cert->principals[i]); 602215116Sdes if (auth_parse_options(pw, line_opts, 603215116Sdes file, linenum) != 1) 604215116Sdes continue; 605323136Sdes found_principal = 1; 606323136Sdes continue; 607215116Sdes } 608215116Sdes } 609215116Sdes } 610323136Sdes return found_principal; 611294336Sdes} 612294336Sdes 613294336Sdesstatic int 614294336Sdesmatch_principals_file(char *file, struct passwd *pw, struct sshkey_cert *cert) 615294336Sdes{ 616294336Sdes FILE *f; 617294336Sdes int success; 618294336Sdes 619294336Sdes temporarily_use_uid(pw); 620294336Sdes debug("trying authorized principals file %s", file); 621294336Sdes if ((f = auth_openprincipals(file, pw, options.strict_modes)) == NULL) { 622294336Sdes restore_uid(); 623294336Sdes return 0; 624294336Sdes } 625294336Sdes success = process_principals(f, file, pw, cert); 626215116Sdes fclose(f); 627215116Sdes restore_uid(); 628294336Sdes return success; 629248619Sdes} 630215116Sdes 631248619Sdes/* 632294336Sdes * Checks whether principal is allowed in output of command. 633294336Sdes * returns 1 if the principal is allowed or 0 otherwise. 634294336Sdes */ 635294336Sdesstatic int 636323134Sdesmatch_principals_command(struct passwd *user_pw, const struct sshkey *key) 637294336Sdes{ 638323134Sdes const struct sshkey_cert *cert = key->cert; 639294336Sdes FILE *f = NULL; 640323134Sdes int r, ok, found_principal = 0; 641294336Sdes struct passwd *pw; 642294336Sdes int i, ac = 0, uid_swapped = 0; 643294336Sdes pid_t pid; 644294336Sdes char *tmp, *username = NULL, *command = NULL, **av = NULL; 645323134Sdes char *ca_fp = NULL, *key_fp = NULL, *catext = NULL, *keytext = NULL; 646323134Sdes char serial_s[16]; 647294336Sdes void (*osigchld)(int); 648294336Sdes 649294336Sdes if (options.authorized_principals_command == NULL) 650294336Sdes return 0; 651294336Sdes if (options.authorized_principals_command_user == NULL) { 652294336Sdes error("No user for AuthorizedPrincipalsCommand specified, " 653294336Sdes "skipping"); 654294336Sdes return 0; 655294336Sdes } 656294336Sdes 657294336Sdes /* 658294336Sdes * NB. all returns later this function should go via "out" to 659294336Sdes * ensure the original SIGCHLD handler is restored properly. 660294336Sdes */ 661294336Sdes osigchld = signal(SIGCHLD, SIG_DFL); 662294336Sdes 663294336Sdes /* Prepare and verify the user for the command */ 664294336Sdes username = percent_expand(options.authorized_principals_command_user, 665294336Sdes "u", user_pw->pw_name, (char *)NULL); 666294336Sdes pw = getpwnam(username); 667294336Sdes if (pw == NULL) { 668294336Sdes error("AuthorizedPrincipalsCommandUser \"%s\" not found: %s", 669294336Sdes username, strerror(errno)); 670294336Sdes goto out; 671294336Sdes } 672294336Sdes 673294336Sdes /* Turn the command into an argument vector */ 674294336Sdes if (split_argv(options.authorized_principals_command, &ac, &av) != 0) { 675294336Sdes error("AuthorizedPrincipalsCommand \"%s\" contains " 676294336Sdes "invalid quotes", command); 677294336Sdes goto out; 678294336Sdes } 679294336Sdes if (ac == 0) { 680294336Sdes error("AuthorizedPrincipalsCommand \"%s\" yielded no arguments", 681294336Sdes command); 682294336Sdes goto out; 683294336Sdes } 684323134Sdes if ((ca_fp = sshkey_fingerprint(cert->signature_key, 685323134Sdes options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { 686323134Sdes error("%s: sshkey_fingerprint failed", __func__); 687323134Sdes goto out; 688323134Sdes } 689323134Sdes if ((key_fp = sshkey_fingerprint(key, 690323134Sdes options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { 691323134Sdes error("%s: sshkey_fingerprint failed", __func__); 692323134Sdes goto out; 693323134Sdes } 694323134Sdes if ((r = sshkey_to_base64(cert->signature_key, &catext)) != 0) { 695323134Sdes error("%s: sshkey_to_base64 failed: %s", __func__, ssh_err(r)); 696323134Sdes goto out; 697323134Sdes } 698323134Sdes if ((r = sshkey_to_base64(key, &keytext)) != 0) { 699323134Sdes error("%s: sshkey_to_base64 failed: %s", __func__, ssh_err(r)); 700323134Sdes goto out; 701323134Sdes } 702323134Sdes snprintf(serial_s, sizeof(serial_s), "%llu", 703323134Sdes (unsigned long long)cert->serial); 704294336Sdes for (i = 1; i < ac; i++) { 705294336Sdes tmp = percent_expand(av[i], 706294336Sdes "u", user_pw->pw_name, 707294336Sdes "h", user_pw->pw_dir, 708323134Sdes "t", sshkey_ssh_name(key), 709323134Sdes "T", sshkey_ssh_name(cert->signature_key), 710323134Sdes "f", key_fp, 711323134Sdes "F", ca_fp, 712323134Sdes "k", keytext, 713323134Sdes "K", catext, 714323134Sdes "i", cert->key_id, 715323134Sdes "s", serial_s, 716294336Sdes (char *)NULL); 717294336Sdes if (tmp == NULL) 718294336Sdes fatal("%s: percent_expand failed", __func__); 719294336Sdes free(av[i]); 720294336Sdes av[i] = tmp; 721294336Sdes } 722294336Sdes /* Prepare a printable command for logs, etc. */ 723294336Sdes command = assemble_argv(ac, av); 724294336Sdes 725294336Sdes if ((pid = subprocess("AuthorizedPrincipalsCommand", pw, command, 726294336Sdes ac, av, &f)) == 0) 727294336Sdes goto out; 728294336Sdes 729294336Sdes uid_swapped = 1; 730294336Sdes temporarily_use_uid(pw); 731294336Sdes 732294336Sdes ok = process_principals(f, NULL, pw, cert); 733294336Sdes 734323136Sdes fclose(f); 735323136Sdes f = NULL; 736323136Sdes 737294336Sdes if (exited_cleanly(pid, "AuthorizedPrincipalsCommand", command) != 0) 738294336Sdes goto out; 739294336Sdes 740294336Sdes /* Read completed successfully */ 741294336Sdes found_principal = ok; 742294336Sdes out: 743294336Sdes if (f != NULL) 744294336Sdes fclose(f); 745294336Sdes signal(SIGCHLD, osigchld); 746294336Sdes for (i = 0; i < ac; i++) 747294336Sdes free(av[i]); 748294336Sdes free(av); 749294336Sdes if (uid_swapped) 750294336Sdes restore_uid(); 751294336Sdes free(command); 752294336Sdes free(username); 753323134Sdes free(ca_fp); 754323134Sdes free(key_fp); 755323134Sdes free(catext); 756323134Sdes free(keytext); 757294336Sdes return found_principal; 758294336Sdes} 759294336Sdes/* 760248619Sdes * Checks whether key is allowed in authorized_keys-format file, 761248619Sdes * returns 1 if the key is allowed or 0 otherwise. 762248619Sdes */ 76398675Sdesstatic int 764248619Sdescheck_authkeys_file(FILE *f, char *file, Key* key, struct passwd *pw) 76598675Sdes{ 766146998Sdes char line[SSH_MAX_PUBKEY_BYTES]; 76798675Sdes int found_key = 0; 76898675Sdes u_long linenum = 0; 76998675Sdes Key *found; 77098675Sdes 77198675Sdes found_key = 0; 77298675Sdes 773255767Sdes found = NULL; 774146998Sdes while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) { 775323134Sdes char *cp, *key_options = NULL, *fp = NULL; 776323134Sdes const char *reason = NULL; 777323134Sdes 778323136Sdes /* Always consume entrire file */ 779323136Sdes if (found_key) 780323136Sdes continue; 781255767Sdes if (found != NULL) 782255767Sdes key_free(found); 783255767Sdes found = key_new(key_is_cert(key) ? KEY_UNSPEC : key->type); 784204917Sdes auth_clear_options(); 785204917Sdes 78698675Sdes /* Skip leading whitespace, empty and comment lines. */ 78798675Sdes for (cp = line; *cp == ' ' || *cp == '\t'; cp++) 78898675Sdes ; 78998675Sdes if (!*cp || *cp == '\n' || *cp == '#') 79098675Sdes continue; 79198675Sdes 79298675Sdes if (key_read(found, &cp) != 1) { 79398675Sdes /* no key? check if there are options for this key */ 79498675Sdes int quoted = 0; 79598675Sdes debug2("user_key_allowed: check options: '%s'", cp); 796137015Sdes key_options = cp; 79798675Sdes for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { 79898675Sdes if (*cp == '\\' && cp[1] == '"') 79998675Sdes cp++; /* Skip both */ 80098675Sdes else if (*cp == '"') 80198675Sdes quoted = !quoted; 80298675Sdes } 80398675Sdes /* Skip remaining whitespace. */ 80498675Sdes for (; *cp == ' ' || *cp == '\t'; cp++) 80598675Sdes ; 80698675Sdes if (key_read(found, &cp) != 1) { 80798675Sdes debug2("user_key_allowed: advance: '%s'", cp); 80898675Sdes /* still no key? advance to next line*/ 80998675Sdes continue; 81098675Sdes } 81198675Sdes } 812215116Sdes if (key_is_cert(key)) { 813215116Sdes if (!key_equal(found, key->cert->signature_key)) 814215116Sdes continue; 815215116Sdes if (auth_parse_options(pw, key_options, file, 816215116Sdes linenum) != 1) 817215116Sdes continue; 818204917Sdes if (!key_is_cert_authority) 819204917Sdes continue; 820294332Sdes if ((fp = sshkey_fingerprint(found, 821294332Sdes options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) 822294332Sdes continue; 823207319Sdes debug("matching CA found: file %s, line %lu, %s %s", 824207319Sdes file, linenum, key_type(found), fp); 825215116Sdes /* 826215116Sdes * If the user has specified a list of principals as 827215116Sdes * a key option, then prefer that list to matching 828215116Sdes * their username in the certificate principals list. 829215116Sdes */ 830215116Sdes if (authorized_principals != NULL && 831215116Sdes !match_principals_option(authorized_principals, 832215116Sdes key->cert)) { 833215116Sdes reason = "Certificate does not contain an " 834215116Sdes "authorized principal"; 835215116Sdes fail_reason: 836255767Sdes free(fp); 837204917Sdes error("%s", reason); 838204917Sdes auth_debug_add("%s", reason); 839204917Sdes continue; 840204917Sdes } 841215116Sdes if (key_cert_check_authority(key, 0, 0, 842215116Sdes authorized_principals == NULL ? pw->pw_name : NULL, 843215116Sdes &reason) != 0) 844215116Sdes goto fail_reason; 845323134Sdes if (auth_cert_options(key, pw, &reason) != 0) 846323134Sdes goto fail_reason; 847296633Sdes verbose("Accepted certificate ID \"%s\" (serial %llu) " 848207319Sdes "signed by %s CA %s via %s", key->cert->key_id, 849296633Sdes (unsigned long long)key->cert->serial, 850207319Sdes key_type(found), fp, file); 851255767Sdes free(fp); 85298675Sdes found_key = 1; 853204917Sdes break; 854215116Sdes } else if (key_equal(found, key)) { 855215116Sdes if (auth_parse_options(pw, key_options, file, 856215116Sdes linenum) != 1) 857215116Sdes continue; 858215116Sdes if (key_is_cert_authority) 859215116Sdes continue; 860294332Sdes if ((fp = sshkey_fingerprint(found, 861294332Sdes options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) 862294332Sdes continue; 863255767Sdes debug("matching key found: file %s, line %lu %s %s", 864255767Sdes file, linenum, key_type(found), fp); 865255767Sdes free(fp); 866294332Sdes found_key = 1; 867323136Sdes continue; 86898675Sdes } 86998675Sdes } 870255767Sdes if (found != NULL) 871255767Sdes key_free(found); 87298675Sdes if (!found_key) 87398675Sdes debug2("key not found"); 87498675Sdes return found_key; 87598675Sdes} 87698675Sdes 877204917Sdes/* Authenticate a certificate key against TrustedUserCAKeys */ 878204917Sdesstatic int 879204917Sdesuser_cert_trusted_ca(struct passwd *pw, Key *key) 880204917Sdes{ 881215116Sdes char *ca_fp, *principals_file = NULL; 882204917Sdes const char *reason; 883294336Sdes int ret = 0, found_principal = 0, use_authorized_principals; 884204917Sdes 885204917Sdes if (!key_is_cert(key) || options.trusted_user_ca_keys == NULL) 886204917Sdes return 0; 887204917Sdes 888294332Sdes if ((ca_fp = sshkey_fingerprint(key->cert->signature_key, 889294332Sdes options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) 890294332Sdes return 0; 891204917Sdes 892294332Sdes if (sshkey_in_file(key->cert->signature_key, 893294332Sdes options.trusted_user_ca_keys, 1, 0) != 0) { 894204917Sdes debug2("%s: CA %s %s is not listed in %s", __func__, 895204917Sdes key_type(key->cert->signature_key), ca_fp, 896204917Sdes options.trusted_user_ca_keys); 897204917Sdes goto out; 898204917Sdes } 899215116Sdes /* 900215116Sdes * If AuthorizedPrincipals is in use, then compare the certificate 901215116Sdes * principals against the names in that file rather than matching 902215116Sdes * against the username. 903215116Sdes */ 904215116Sdes if ((principals_file = authorized_principals_file(pw)) != NULL) { 905294336Sdes if (match_principals_file(principals_file, pw, key->cert)) 906294336Sdes found_principal = 1; 907294336Sdes } 908294336Sdes /* Try querying command if specified */ 909323134Sdes if (!found_principal && match_principals_command(pw, key)) 910294336Sdes found_principal = 1; 911294336Sdes /* If principals file or command is specified, then require a match */ 912294336Sdes use_authorized_principals = principals_file != NULL || 913294336Sdes options.authorized_principals_command != NULL; 914294336Sdes if (!found_principal && use_authorized_principals) { 915294336Sdes reason = "Certificate does not contain an authorized principal"; 916215116Sdes fail_reason: 917294336Sdes error("%s", reason); 918294336Sdes auth_debug_add("%s", reason); 919294336Sdes goto out; 920204917Sdes } 921215116Sdes if (key_cert_check_authority(key, 0, 1, 922294336Sdes use_authorized_principals ? NULL : pw->pw_name, &reason) != 0) 923215116Sdes goto fail_reason; 924323134Sdes if (auth_cert_options(key, pw, &reason) != 0) 925323134Sdes goto fail_reason; 926204917Sdes 927296633Sdes verbose("Accepted certificate ID \"%s\" (serial %llu) signed by " 928296633Sdes "%s CA %s via %s", key->cert->key_id, 929296633Sdes (unsigned long long)key->cert->serial, 930296633Sdes key_type(key->cert->signature_key), ca_fp, 931207319Sdes options.trusted_user_ca_keys); 932204917Sdes ret = 1; 933204917Sdes 934204917Sdes out: 935255767Sdes free(principals_file); 936255767Sdes free(ca_fp); 937204917Sdes return ret; 938204917Sdes} 939204917Sdes 940248619Sdes/* 941248619Sdes * Checks whether key is allowed in file. 942248619Sdes * returns 1 if the key is allowed or 0 otherwise. 943248619Sdes */ 944248619Sdesstatic int 945248619Sdesuser_key_allowed2(struct passwd *pw, Key *key, char *file) 946248619Sdes{ 947248619Sdes FILE *f; 948248619Sdes int found_key = 0; 949248619Sdes 950248619Sdes /* Temporarily use the user's uid. */ 951248619Sdes temporarily_use_uid(pw); 952248619Sdes 953248619Sdes debug("trying public key file %s", file); 954248619Sdes if ((f = auth_openkeyfile(file, pw, options.strict_modes)) != NULL) { 955248619Sdes found_key = check_authkeys_file(f, file, key, pw); 956248619Sdes fclose(f); 957248619Sdes } 958248619Sdes 959248619Sdes restore_uid(); 960248619Sdes return found_key; 961248619Sdes} 962248619Sdes 963248619Sdes/* 964248619Sdes * Checks whether key is allowed in output of command. 965248619Sdes * returns 1 if the key is allowed or 0 otherwise. 966248619Sdes */ 967248619Sdesstatic int 968248619Sdesuser_key_command_allowed2(struct passwd *user_pw, Key *key) 969248619Sdes{ 970294336Sdes FILE *f = NULL; 971294336Sdes int r, ok, found_key = 0; 972248619Sdes struct passwd *pw; 973294336Sdes int i, uid_swapped = 0, ac = 0; 974248619Sdes pid_t pid; 975294336Sdes char *username = NULL, *key_fp = NULL, *keytext = NULL; 976294336Sdes char *tmp, *command = NULL, **av = NULL; 977294336Sdes void (*osigchld)(int); 978248619Sdes 979294336Sdes if (options.authorized_keys_command == NULL) 980248619Sdes return 0; 981248619Sdes if (options.authorized_keys_command_user == NULL) { 982248619Sdes error("No user for AuthorizedKeysCommand specified, skipping"); 983248619Sdes return 0; 984248619Sdes } 985248619Sdes 986294336Sdes /* 987294336Sdes * NB. all returns later this function should go via "out" to 988294336Sdes * ensure the original SIGCHLD handler is restored properly. 989294336Sdes */ 990294336Sdes osigchld = signal(SIGCHLD, SIG_DFL); 991294336Sdes 992294336Sdes /* Prepare and verify the user for the command */ 993248619Sdes username = percent_expand(options.authorized_keys_command_user, 994248619Sdes "u", user_pw->pw_name, (char *)NULL); 995248619Sdes pw = getpwnam(username); 996248619Sdes if (pw == NULL) { 997248619Sdes error("AuthorizedKeysCommandUser \"%s\" not found: %s", 998248619Sdes username, strerror(errno)); 999294336Sdes goto out; 1000248619Sdes } 1001248619Sdes 1002294336Sdes /* Prepare AuthorizedKeysCommand */ 1003294336Sdes if ((key_fp = sshkey_fingerprint(key, options.fingerprint_hash, 1004294336Sdes SSH_FP_DEFAULT)) == NULL) { 1005294336Sdes error("%s: sshkey_fingerprint failed", __func__); 1006248619Sdes goto out; 1007248619Sdes } 1008294336Sdes if ((r = sshkey_to_base64(key, &keytext)) != 0) { 1009294336Sdes error("%s: sshkey_to_base64 failed: %s", __func__, ssh_err(r)); 1010248619Sdes goto out; 1011248619Sdes } 1012248619Sdes 1013294336Sdes /* Turn the command into an argument vector */ 1014294336Sdes if (split_argv(options.authorized_keys_command, &ac, &av) != 0) { 1015294336Sdes error("AuthorizedKeysCommand \"%s\" contains invalid quotes", 1016294336Sdes command); 1017248619Sdes goto out; 1018248619Sdes } 1019294336Sdes if (ac == 0) { 1020294336Sdes error("AuthorizedKeysCommand \"%s\" yielded no arguments", 1021294336Sdes command); 1022294336Sdes goto out; 1023294336Sdes } 1024294336Sdes for (i = 1; i < ac; i++) { 1025294336Sdes tmp = percent_expand(av[i], 1026294336Sdes "u", user_pw->pw_name, 1027294336Sdes "h", user_pw->pw_dir, 1028294336Sdes "t", sshkey_ssh_name(key), 1029294336Sdes "f", key_fp, 1030294336Sdes "k", keytext, 1031294336Sdes (char *)NULL); 1032294336Sdes if (tmp == NULL) 1033294336Sdes fatal("%s: percent_expand failed", __func__); 1034294336Sdes free(av[i]); 1035294336Sdes av[i] = tmp; 1036294336Sdes } 1037294336Sdes /* Prepare a printable command for logs, etc. */ 1038294336Sdes command = assemble_argv(ac, av); 1039248619Sdes 1040248619Sdes /* 1041294336Sdes * If AuthorizedKeysCommand was run without arguments 1042294336Sdes * then fall back to the old behaviour of passing the 1043294336Sdes * target username as a single argument. 1044248619Sdes */ 1045294336Sdes if (ac == 1) { 1046294336Sdes av = xreallocarray(av, ac + 2, sizeof(*av)); 1047294336Sdes av[1] = xstrdup(user_pw->pw_name); 1048294336Sdes av[2] = NULL; 1049294336Sdes /* Fix up command too, since it is used in log messages */ 1050294336Sdes free(command); 1051294336Sdes xasprintf(&command, "%s %s", av[0], av[1]); 1052294336Sdes } 1053248619Sdes 1054294336Sdes if ((pid = subprocess("AuthorizedKeysCommand", pw, command, 1055294336Sdes ac, av, &f)) == 0) 1056294336Sdes goto out; 1057248619Sdes 1058294336Sdes uid_swapped = 1; 1059248619Sdes temporarily_use_uid(pw); 1060248619Sdes 1061248619Sdes ok = check_authkeys_file(f, options.authorized_keys_command, key, pw); 1062248619Sdes 1063323136Sdes fclose(f); 1064323136Sdes f = NULL; 1065323136Sdes 1066294336Sdes if (exited_cleanly(pid, "AuthorizedKeysCommand", command) != 0) 1067248619Sdes goto out; 1068294336Sdes 1069294336Sdes /* Read completed successfully */ 1070248619Sdes found_key = ok; 1071248619Sdes out: 1072294336Sdes if (f != NULL) 1073294336Sdes fclose(f); 1074294336Sdes signal(SIGCHLD, osigchld); 1075294336Sdes for (i = 0; i < ac; i++) 1076294336Sdes free(av[i]); 1077294336Sdes free(av); 1078294336Sdes if (uid_swapped) 1079294336Sdes restore_uid(); 1080294336Sdes free(command); 1081294336Sdes free(username); 1082294336Sdes free(key_fp); 1083294336Sdes free(keytext); 1084248619Sdes return found_key; 1085248619Sdes} 1086248619Sdes 1087248619Sdes/* 1088248619Sdes * Check whether key authenticates and authorises the user. 1089248619Sdes */ 109098675Sdesint 1091294336Sdesuser_key_allowed(struct passwd *pw, Key *key, int auth_attempt) 109298675Sdes{ 1093226046Sdes u_int success, i; 109498675Sdes char *file; 109598675Sdes 1096204917Sdes if (auth_key_is_revoked(key)) 1097204917Sdes return 0; 1098204917Sdes if (key_is_cert(key) && auth_key_is_revoked(key->cert->signature_key)) 1099204917Sdes return 0; 1100204917Sdes 1101204917Sdes success = user_cert_trusted_ca(pw, key); 1102204917Sdes if (success) 1103204917Sdes return success; 1104204917Sdes 1105248619Sdes success = user_key_command_allowed2(pw, key); 1106248619Sdes if (success > 0) 1107248619Sdes return success; 1108248619Sdes 1109226046Sdes for (i = 0; !success && i < options.num_authkeys_files; i++) { 1110248619Sdes 1111248619Sdes if (strcasecmp(options.authorized_keys_files[i], "none") == 0) 1112248619Sdes continue; 1113226046Sdes file = expand_authorized_keys( 1114226046Sdes options.authorized_keys_files[i], pw); 1115248619Sdes 1116226046Sdes success = user_key_allowed2(pw, key, file); 1117255767Sdes free(file); 1118226046Sdes } 111998675Sdes 112098675Sdes return success; 112198675Sdes} 112298675Sdes 1123294332Sdes/* Records a public key in the list of previously-successful keys */ 1124294332Sdesvoid 1125294332Sdesauth2_record_userkey(Authctxt *authctxt, struct sshkey *key) 1126294332Sdes{ 1127294332Sdes struct sshkey **tmp; 1128294332Sdes 1129294332Sdes if (authctxt->nprev_userkeys >= INT_MAX || 1130294332Sdes (tmp = reallocarray(authctxt->prev_userkeys, 1131294332Sdes authctxt->nprev_userkeys + 1, sizeof(*tmp))) == NULL) 1132294332Sdes fatal("%s: reallocarray failed", __func__); 1133294332Sdes authctxt->prev_userkeys = tmp; 1134294332Sdes authctxt->prev_userkeys[authctxt->nprev_userkeys] = key; 1135294332Sdes authctxt->nprev_userkeys++; 1136294332Sdes} 1137294332Sdes 1138294332Sdes/* Checks whether a key has already been used successfully for authentication */ 1139294332Sdesint 1140294332Sdesauth2_userkey_already_used(Authctxt *authctxt, struct sshkey *key) 1141294332Sdes{ 1142294332Sdes u_int i; 1143294332Sdes 1144294332Sdes for (i = 0; i < authctxt->nprev_userkeys; i++) { 1145294332Sdes if (sshkey_equal_public(key, authctxt->prev_userkeys[i])) { 1146294332Sdes return 1; 1147294332Sdes } 1148294332Sdes } 1149294332Sdes return 0; 1150294332Sdes} 1151294332Sdes 115298675SdesAuthmethod method_pubkey = { 115398675Sdes "publickey", 115498675Sdes userauth_pubkey, 115598675Sdes &options.pubkey_authentication 115698675Sdes}; 1157