1 /* $NetBSD: auth2-pubkey.c,v 1.35 2024/07/08 22:33:43 christos Exp $ */
2 /* $OpenBSD: auth2-pubkey.c,v 1.120 2024/05/17 00:30:23 djm Exp $ */
3
4 /*
5 * Copyright (c) 2000 Markus Friedl. All rights reserved.
6 * Copyright (c) 2010 Damien Miller. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "includes.h"
30 __RCSID("$NetBSD: auth2-pubkey.c,v 1.35 2024/07/08 22:33:43 christos Exp $");
31 #include <sys/types.h>
32
33 #include <stdlib.h>
34 #include <errno.h>
35 #include <paths.h>
36 #include <pwd.h>
37 #include <signal.h>
38 #include <stdio.h>
39 #include <stdarg.h>
40 #include <string.h>
41 #include <time.h>
42 #include <unistd.h>
43 #include <limits.h>
44
45 #include "xmalloc.h"
46 #include "ssh.h"
47 #include "ssh2.h"
48 #include "packet.h"
49 #include "kex.h"
50 #include "sshbuf.h"
51 #include "log.h"
52 #include "misc.h"
53 #include "servconf.h"
54 #include "compat.h"
55 #include "sshkey.h"
56 #include "hostfile.h"
57 #include "auth.h"
58 #include "pathnames.h"
59 #include "uidswap.h"
60 #include "auth-options.h"
61 #include "canohost.h"
62 #ifdef GSSAPI
63 #include "ssh-gss.h"
64 #endif
65 #include "monitor_wrap.h"
66 #include "authfile.h"
67 #include "match.h"
68 #include "digest.h"
69
70 #ifdef WITH_LDAP_PUBKEY
71 #include "ldapauth.h"
72 #endif
73
74 #include "ssherr.h"
75 #include "channels.h" /* XXX for session.h */
76 #include "session.h" /* XXX for child_set_env(); refactor? */
77 #include "sk-api.h"
78
79 /* import */
80 extern ServerOptions options;
81 extern struct authmethod_cfg methodcfg_pubkey;
82
83 static char *
format_key(const struct sshkey * key)84 format_key(const struct sshkey *key)
85 {
86 char *ret, *fp = sshkey_fingerprint(key,
87 options.fingerprint_hash, SSH_FP_DEFAULT);
88
89 xasprintf(&ret, "%s %s", sshkey_type(key), fp);
90 free(fp);
91 return ret;
92 }
93
94 static int
userauth_pubkey(struct ssh * ssh,const char * method)95 userauth_pubkey(struct ssh *ssh, const char *method)
96 {
97 Authctxt *authctxt = ssh->authctxt;
98 struct passwd *pw = authctxt->pw;
99 struct sshbuf *b = NULL;
100 struct sshkey *key = NULL, *hostkey = NULL;
101 char *pkalg = NULL, *userstyle = NULL, *key_s = NULL, *ca_s = NULL;
102 u_char *pkblob = NULL, *sig = NULL, have_sig;
103 size_t blen, slen;
104 int hostbound, r, pktype;
105 int req_presence = 0, req_verify = 0, authenticated = 0;
106 struct sshauthopt *authopts = NULL;
107 struct sshkey_sig_details *sig_details = NULL;
108
109 hostbound = strcmp(method, "publickey-hostbound-v00@openssh.com") == 0;
110
111 if ((r = sshpkt_get_u8(ssh, &have_sig)) != 0 ||
112 (r = sshpkt_get_cstring(ssh, &pkalg, NULL)) != 0 ||
113 (r = sshpkt_get_string(ssh, &pkblob, &blen)) != 0)
114 fatal_fr(r, "parse %s packet", method);
115
116 /* hostbound auth includes the hostkey offered at initial KEX */
117 if (hostbound) {
118 if ((r = sshpkt_getb_froms(ssh, &b)) != 0 ||
119 (r = sshkey_fromb(b, &hostkey)) != 0)
120 fatal_fr(r, "parse %s hostkey", method);
121 if (ssh->kex->initial_hostkey == NULL)
122 fatal_f("internal error: initial hostkey not recorded");
123 if (!sshkey_equal(hostkey, ssh->kex->initial_hostkey))
124 fatal_f("%s packet contained wrong host key", method);
125 sshbuf_free(b);
126 b = NULL;
127 }
128
129 if (log_level_get() >= SYSLOG_LEVEL_DEBUG2) {
130 char *keystring;
131 struct sshbuf *pkbuf;
132
133 if ((pkbuf = sshbuf_from(pkblob, blen)) == NULL)
134 fatal_f("sshbuf_from failed");
135 if ((keystring = sshbuf_dtob64_string(pkbuf, 0)) == NULL)
136 fatal_f("sshbuf_dtob64 failed");
137 debug2_f("%s user %s %s public key %s %s",
138 authctxt->valid ? "valid" : "invalid", authctxt->user,
139 have_sig ? "attempting" : "querying", pkalg, keystring);
140 sshbuf_free(pkbuf);
141 free(keystring);
142 }
143
144 pktype = sshkey_type_from_name(pkalg);
145 if (pktype == KEY_UNSPEC) {
146 /* this is perfectly legal */
147 verbose_f("unsupported public key algorithm: %s", pkalg);
148 goto done;
149 }
150 if ((r = sshkey_from_blob(pkblob, blen, &key)) != 0) {
151 error_fr(r, "parse key");
152 goto done;
153 }
154 if (key == NULL) {
155 error_f("cannot decode key: %s", pkalg);
156 goto done;
157 }
158 if (key->type != pktype) {
159 error_f("type mismatch for decoded key "
160 "(received %d, expected %d)", key->type, pktype);
161 goto done;
162 }
163 if (auth2_key_already_used(authctxt, key)) {
164 logit("refusing previously-used %s key", sshkey_type(key));
165 goto done;
166 }
167 if (match_pattern_list(pkalg, options.pubkey_accepted_algos, 0) != 1) {
168 logit_f("signature algorithm %s not in "
169 "PubkeyAcceptedAlgorithms", pkalg);
170 goto done;
171 }
172 if ((r = sshkey_check_cert_sigtype(key,
173 options.ca_sign_algorithms)) != 0) {
174 logit_fr(r, "certificate signature algorithm %s",
175 (key->cert == NULL || key->cert->signature_type == NULL) ?
176 "(null)" : key->cert->signature_type);
177 goto done;
178 }
179 if ((r = sshkey_check_rsa_length(key,
180 options.required_rsa_size)) != 0) {
181 logit_r(r, "refusing %s key", sshkey_type(key));
182 goto done;
183 }
184 key_s = format_key(key);
185 if (sshkey_is_cert(key))
186 ca_s = format_key(key->cert->signature_key);
187
188 if (have_sig) {
189 debug3_f("%s have %s signature for %s%s%s",
190 method, pkalg, key_s,
191 ca_s == NULL ? "" : " CA ", ca_s == NULL ? "" : ca_s);
192 if ((r = sshpkt_get_string(ssh, &sig, &slen)) != 0 ||
193 (r = sshpkt_get_end(ssh)) != 0)
194 fatal_fr(r, "parse signature packet");
195 if ((b = sshbuf_new()) == NULL)
196 fatal_f("sshbuf_new failed");
197 if (ssh->compat & SSH_OLD_SESSIONID) {
198 if ((r = sshbuf_putb(b, ssh->kex->session_id)) != 0)
199 fatal_fr(r, "put old session id");
200 } else {
201 if ((r = sshbuf_put_stringb(b,
202 ssh->kex->session_id)) != 0)
203 fatal_fr(r, "put session id");
204 }
205 if (!authctxt->valid || authctxt->user == NULL) {
206 debug2_f("disabled because of invalid user");
207 goto done;
208 }
209 /* reconstruct packet */
210 xasprintf(&userstyle, "%s%s%s", authctxt->user,
211 authctxt->style ? ":" : "",
212 authctxt->style ? authctxt->style : "");
213 if ((r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
214 (r = sshbuf_put_cstring(b, userstyle)) != 0 ||
215 (r = sshbuf_put_cstring(b, authctxt->service)) != 0 ||
216 (r = sshbuf_put_cstring(b, method)) != 0 ||
217 (r = sshbuf_put_u8(b, have_sig)) != 0 ||
218 (r = sshbuf_put_cstring(b, pkalg)) != 0 ||
219 (r = sshbuf_put_string(b, pkblob, blen)) != 0)
220 fatal_fr(r, "reconstruct %s packet", method);
221 if (hostbound &&
222 (r = sshkey_puts(ssh->kex->initial_hostkey, b)) != 0)
223 fatal_fr(r, "reconstruct %s packet", method);
224 #ifdef DEBUG_PK
225 sshbuf_dump(b, stderr);
226 #endif
227 /* test for correct signature */
228 authenticated = 0;
229 if (mm_user_key_allowed(ssh, pw, key, 1, &authopts) &&
230 mm_sshkey_verify(key, sig, slen,
231 sshbuf_ptr(b), sshbuf_len(b),
232 (ssh->compat & SSH_BUG_SIGTYPE) == 0 ? pkalg : NULL,
233 ssh->compat, &sig_details) == 0) {
234 authenticated = 1;
235 }
236 if (authenticated == 1 && sig_details != NULL) {
237 auth2_record_info(authctxt, "signature count = %u",
238 sig_details->sk_counter);
239 debug_f("sk_counter = %u, sk_flags = 0x%02x",
240 sig_details->sk_counter, sig_details->sk_flags);
241 req_presence = (options.pubkey_auth_options &
242 PUBKEYAUTH_TOUCH_REQUIRED) ||
243 !authopts->no_require_user_presence;
244 if (req_presence && (sig_details->sk_flags &
245 SSH_SK_USER_PRESENCE_REQD) == 0) {
246 error("public key %s signature for %s%s from "
247 "%.128s port %d rejected: user presence "
248 "(authenticator touch) requirement "
249 "not met ", key_s,
250 authctxt->valid ? "" : "invalid user ",
251 authctxt->user, ssh_remote_ipaddr(ssh),
252 ssh_remote_port(ssh));
253 authenticated = 0;
254 goto done;
255 }
256 req_verify = (options.pubkey_auth_options &
257 PUBKEYAUTH_VERIFY_REQUIRED) ||
258 authopts->require_verify;
259 if (req_verify && (sig_details->sk_flags &
260 SSH_SK_USER_VERIFICATION_REQD) == 0) {
261 error("public key %s signature for %s%s from "
262 "%.128s port %d rejected: user "
263 "verification requirement not met ", key_s,
264 authctxt->valid ? "" : "invalid user ",
265 authctxt->user, ssh_remote_ipaddr(ssh),
266 ssh_remote_port(ssh));
267 authenticated = 0;
268 goto done;
269 }
270 }
271 auth2_record_key(authctxt, authenticated, key);
272 } else {
273 debug_f("%s test pkalg %s pkblob %s%s%s", method, pkalg, key_s,
274 ca_s == NULL ? "" : " CA ", ca_s == NULL ? "" : ca_s);
275
276 if ((r = sshpkt_get_end(ssh)) != 0)
277 fatal_fr(r, "parse packet");
278
279 if (!authctxt->valid || authctxt->user == NULL) {
280 debug2_f("disabled because of invalid user");
281 goto done;
282 }
283 /* XXX fake reply and always send PK_OK ? */
284 /*
285 * XXX this allows testing whether a user is allowed
286 * to login: if you happen to have a valid pubkey this
287 * message is sent. the message is NEVER sent at all
288 * if a user is not allowed to login. is this an
289 * issue? -markus
290 */
291 if (mm_user_key_allowed(ssh, pw, key, 0, NULL)) {
292 if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_PK_OK))
293 != 0 ||
294 (r = sshpkt_put_cstring(ssh, pkalg)) != 0 ||
295 (r = sshpkt_put_string(ssh, pkblob, blen)) != 0 ||
296 (r = sshpkt_send(ssh)) != 0 ||
297 (r = ssh_packet_write_wait(ssh)) < 0)
298 fatal_fr(r, "send packet");
299 authctxt->postponed = 1;
300 }
301 }
302 done:
303 if (authenticated == 1 && auth_activate_options(ssh, authopts) != 0) {
304 debug_f("key options inconsistent with existing");
305 authenticated = 0;
306 }
307 debug2_f("authenticated %d pkalg %s", authenticated, pkalg);
308
309 sshbuf_free(b);
310 sshauthopt_free(authopts);
311 sshkey_free(key);
312 sshkey_free(hostkey);
313 free(userstyle);
314 free(pkalg);
315 free(pkblob);
316 free(key_s);
317 free(ca_s);
318 free(sig);
319 sshkey_sig_details_free(sig_details);
320 return authenticated;
321 }
322
323 static int
match_principals_file(struct passwd * pw,char * file,struct sshkey_cert * cert,struct sshauthopt ** authoptsp)324 match_principals_file(struct passwd *pw, char *file,
325 struct sshkey_cert *cert, struct sshauthopt **authoptsp)
326 {
327 FILE *f;
328 int success;
329
330 if (authoptsp != NULL)
331 *authoptsp = NULL;
332
333 temporarily_use_uid(pw);
334 debug("trying authorized principals file %s", file);
335 if ((f = auth_openprincipals(file, pw, options.strict_modes)) == NULL) {
336 restore_uid();
337 return 0;
338 }
339 success = auth_process_principals(f, file, cert, authoptsp);
340 fclose(f);
341 restore_uid();
342 return success;
343 }
344
345 /*
346 * Checks whether principal is allowed in output of command.
347 * returns 1 if the principal is allowed or 0 otherwise.
348 */
349 static int
match_principals_command(struct passwd * user_pw,const struct sshkey * key,const char * conn_id,const char * rdomain,struct sshauthopt ** authoptsp)350 match_principals_command(struct passwd *user_pw, const struct sshkey *key,
351 const char *conn_id, const char *rdomain, struct sshauthopt **authoptsp)
352 {
353 struct passwd *runas_pw = NULL;
354 const struct sshkey_cert *cert = key->cert;
355 FILE *f = NULL;
356 int r, ok, found_principal = 0;
357 int i, ac = 0, uid_swapped = 0;
358 pid_t pid;
359 char *tmp, *username = NULL, *command = NULL, **av = NULL;
360 char *ca_fp = NULL, *key_fp = NULL, *catext = NULL, *keytext = NULL;
361 char serial_s[32], uidstr[32];
362 void (*osigchld)(int);
363
364 if (authoptsp != NULL)
365 *authoptsp = NULL;
366 if (options.authorized_principals_command == NULL)
367 return 0;
368 if (options.authorized_principals_command_user == NULL) {
369 error("No user for AuthorizedPrincipalsCommand specified, "
370 "skipping");
371 return 0;
372 }
373
374 /*
375 * NB. all returns later this function should go via "out" to
376 * ensure the original SIGCHLD handler is restored properly.
377 */
378 osigchld = ssh_signal(SIGCHLD, SIG_DFL);
379
380 /* Prepare and verify the user for the command */
381 username = percent_expand(options.authorized_principals_command_user,
382 "u", user_pw->pw_name, (char *)NULL);
383 runas_pw = getpwnam(username);
384 if (runas_pw == NULL) {
385 error("AuthorizedPrincipalsCommandUser \"%s\" not found: %s",
386 username, strerror(errno));
387 goto out;
388 }
389
390 /* Turn the command into an argument vector */
391 if (argv_split(options.authorized_principals_command,
392 &ac, &av, 0) != 0) {
393 error("AuthorizedPrincipalsCommand \"%s\" contains "
394 "invalid quotes", options.authorized_principals_command);
395 goto out;
396 }
397 if (ac == 0) {
398 error("AuthorizedPrincipalsCommand \"%s\" yielded no arguments",
399 options.authorized_principals_command);
400 goto out;
401 }
402 if ((ca_fp = sshkey_fingerprint(cert->signature_key,
403 options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) {
404 error_f("sshkey_fingerprint failed");
405 goto out;
406 }
407 if ((key_fp = sshkey_fingerprint(key,
408 options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) {
409 error_f("sshkey_fingerprint failed");
410 goto out;
411 }
412 if ((r = sshkey_to_base64(cert->signature_key, &catext)) != 0) {
413 error_fr(r, "sshkey_to_base64 failed");
414 goto out;
415 }
416 if ((r = sshkey_to_base64(key, &keytext)) != 0) {
417 error_fr(r, "sshkey_to_base64 failed");
418 goto out;
419 }
420 snprintf(serial_s, sizeof(serial_s), "%llu",
421 (unsigned long long)cert->serial);
422 snprintf(uidstr, sizeof(uidstr), "%llu",
423 (unsigned long long)user_pw->pw_uid);
424 for (i = 1; i < ac; i++) {
425 tmp = percent_expand(av[i],
426 "C", conn_id,
427 "D", rdomain,
428 "U", uidstr,
429 "u", user_pw->pw_name,
430 "h", user_pw->pw_dir,
431 "t", sshkey_ssh_name(key),
432 "T", sshkey_ssh_name(cert->signature_key),
433 "f", key_fp,
434 "F", ca_fp,
435 "k", keytext,
436 "K", catext,
437 "i", cert->key_id,
438 "s", serial_s,
439 (char *)NULL);
440 if (tmp == NULL)
441 fatal_f("percent_expand failed");
442 free(av[i]);
443 av[i] = tmp;
444 }
445 /* Prepare a printable command for logs, etc. */
446 command = argv_assemble(ac, av);
447
448 if ((pid = subprocess("AuthorizedPrincipalsCommand", command,
449 ac, av, &f,
450 SSH_SUBPROCESS_STDOUT_CAPTURE|SSH_SUBPROCESS_STDERR_DISCARD,
451 runas_pw, temporarily_use_uid, restore_uid)) == 0)
452 goto out;
453
454 uid_swapped = 1;
455 temporarily_use_uid(runas_pw);
456
457 ok = auth_process_principals(f, "(command)", cert, authoptsp);
458
459 fclose(f);
460 f = NULL;
461
462 if (exited_cleanly(pid, "AuthorizedPrincipalsCommand", command, 0) != 0)
463 goto out;
464
465 /* Read completed successfully */
466 found_principal = ok;
467 out:
468 if (f != NULL)
469 fclose(f);
470 ssh_signal(SIGCHLD, osigchld);
471 for (i = 0; i < ac; i++)
472 free(av[i]);
473 free(av);
474 if (uid_swapped)
475 restore_uid();
476 free(command);
477 free(username);
478 free(ca_fp);
479 free(key_fp);
480 free(catext);
481 free(keytext);
482 return found_principal;
483 }
484
485 /* Authenticate a certificate key against TrustedUserCAKeys */
486 static int
user_cert_trusted_ca(struct passwd * pw,struct sshkey * key,const char * remote_ip,const char * remote_host,const char * conn_id,const char * rdomain,struct sshauthopt ** authoptsp)487 user_cert_trusted_ca(struct passwd *pw, struct sshkey *key,
488 const char *remote_ip, const char *remote_host,
489 const char *conn_id, const char *rdomain, struct sshauthopt **authoptsp)
490 {
491 char *ca_fp, *principals_file = NULL;
492 const char *reason;
493 struct sshauthopt *principals_opts = NULL, *cert_opts = NULL;
494 struct sshauthopt *final_opts = NULL;
495 int r, ret = 0, found_principal = 0, use_authorized_principals;
496
497 if (authoptsp != NULL)
498 *authoptsp = NULL;
499
500 if (!sshkey_is_cert(key) || options.trusted_user_ca_keys == NULL)
501 return 0;
502
503 if ((ca_fp = sshkey_fingerprint(key->cert->signature_key,
504 options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
505 return 0;
506
507 if ((r = sshkey_in_file(key->cert->signature_key,
508 options.trusted_user_ca_keys, 1, 0)) != 0) {
509 debug2_fr(r, "CA %s %s is not listed in %s",
510 sshkey_type(key->cert->signature_key), ca_fp,
511 options.trusted_user_ca_keys);
512 goto out;
513 }
514 /*
515 * If AuthorizedPrincipals is in use, then compare the certificate
516 * principals against the names in that file rather than matching
517 * against the username.
518 */
519 if ((principals_file = authorized_principals_file(pw)) != NULL) {
520 if (match_principals_file(pw, principals_file,
521 key->cert, &principals_opts))
522 found_principal = 1;
523 }
524 /* Try querying command if specified */
525 if (!found_principal && match_principals_command(pw, key,
526 conn_id, rdomain, &principals_opts))
527 found_principal = 1;
528 /* If principals file or command is specified, then require a match */
529 use_authorized_principals = principals_file != NULL ||
530 options.authorized_principals_command != NULL;
531 if (!found_principal && use_authorized_principals) {
532 reason = "Certificate does not contain an authorized principal";
533 goto fail_reason;
534 }
535 if (use_authorized_principals && principals_opts == NULL)
536 fatal_f("internal error: missing principals_opts");
537 if (sshkey_cert_check_authority_now(key, 0, 1, 0,
538 use_authorized_principals ? NULL : pw->pw_name, &reason) != 0)
539 goto fail_reason;
540
541 /* Check authority from options in key and from principals file/cmd */
542 if ((cert_opts = sshauthopt_from_cert(key)) == NULL) {
543 reason = "Invalid certificate options";
544 goto fail_reason;
545 }
546 if (auth_authorise_keyopts(pw, cert_opts, 0,
547 remote_ip, remote_host, "cert") != 0) {
548 reason = "Refused by certificate options";
549 goto fail_reason;
550 }
551 if (principals_opts == NULL) {
552 final_opts = cert_opts;
553 cert_opts = NULL;
554 } else {
555 if (auth_authorise_keyopts(pw, principals_opts, 0,
556 remote_ip, remote_host, "principals") != 0) {
557 reason = "Refused by certificate principals options";
558 goto fail_reason;
559 }
560 if ((final_opts = sshauthopt_merge(principals_opts,
561 cert_opts, &reason)) == NULL) {
562 fail_reason:
563 error("%s", reason);
564 auth_debug_add("%s", reason);
565 goto out;
566 }
567 }
568
569 /* Success */
570 verbose("Accepted certificate ID \"%s\" (serial %llu) signed by "
571 "%s CA %s via %s", key->cert->key_id,
572 (unsigned long long)key->cert->serial,
573 sshkey_type(key->cert->signature_key), ca_fp,
574 options.trusted_user_ca_keys);
575 if (authoptsp != NULL) {
576 *authoptsp = final_opts;
577 final_opts = NULL;
578 }
579 ret = 1;
580 out:
581 sshauthopt_free(principals_opts);
582 sshauthopt_free(cert_opts);
583 sshauthopt_free(final_opts);
584 free(principals_file);
585 free(ca_fp);
586 return ret;
587 }
588
589 /*
590 * Checks whether key is allowed in file.
591 * returns 1 if the key is allowed or 0 otherwise.
592 */
593 static int
user_key_allowed2(struct passwd * pw,struct sshkey * key,char * file,const char * remote_ip,const char * remote_host,struct sshauthopt ** authoptsp)594 user_key_allowed2(struct passwd *pw, struct sshkey *key,
595 char *file, const char *remote_ip, const char *remote_host,
596 struct sshauthopt **authoptsp)
597 {
598 FILE *f;
599 int found_key = 0;
600
601 if (authoptsp != NULL)
602 *authoptsp = NULL;
603
604 /* Temporarily use the user's uid. */
605 temporarily_use_uid(pw);
606
607 debug("trying public key file %s", file);
608 if ((f = auth_openkeyfile(file, pw, options.strict_modes)) != NULL) {
609 found_key = auth_check_authkeys_file(pw, f, file,
610 key, remote_ip, remote_host, authoptsp);
611 fclose(f);
612 }
613
614 restore_uid();
615 return found_key;
616 }
617
618 /*
619 * Checks whether key is allowed in output of command.
620 * returns 1 if the key is allowed or 0 otherwise.
621 */
622 static int
user_key_command_allowed2(struct passwd * user_pw,struct sshkey * key,const char * remote_ip,const char * remote_host,const char * conn_id,const char * rdomain,struct sshauthopt ** authoptsp)623 user_key_command_allowed2(struct passwd *user_pw, struct sshkey *key,
624 const char *remote_ip, const char *remote_host,
625 const char *conn_id, const char *rdomain, struct sshauthopt **authoptsp)
626 {
627 struct passwd *runas_pw = NULL;
628 FILE *f = NULL;
629 int r, ok, found_key = 0;
630 int i, uid_swapped = 0, ac = 0;
631 pid_t pid;
632 char *username = NULL, *key_fp = NULL, *keytext = NULL;
633 char uidstr[32], *tmp, *command = NULL, **av = NULL;
634 void (*osigchld)(int);
635
636 if (authoptsp != NULL)
637 *authoptsp = NULL;
638 if (options.authorized_keys_command == NULL)
639 return 0;
640 if (options.authorized_keys_command_user == NULL) {
641 error("No user for AuthorizedKeysCommand specified, skipping");
642 return 0;
643 }
644
645 /*
646 * NB. all returns later this function should go via "out" to
647 * ensure the original SIGCHLD handler is restored properly.
648 */
649 osigchld = ssh_signal(SIGCHLD, SIG_DFL);
650
651 /* Prepare and verify the user for the command */
652 username = percent_expand(options.authorized_keys_command_user,
653 "u", user_pw->pw_name, (char *)NULL);
654 runas_pw = getpwnam(username);
655 if (runas_pw == NULL) {
656 error("AuthorizedKeysCommandUser \"%s\" not found: %s",
657 username, strerror(errno));
658 goto out;
659 }
660
661 /* Prepare AuthorizedKeysCommand */
662 if ((key_fp = sshkey_fingerprint(key, options.fingerprint_hash,
663 SSH_FP_DEFAULT)) == NULL) {
664 error_f("sshkey_fingerprint failed");
665 goto out;
666 }
667 if ((r = sshkey_to_base64(key, &keytext)) != 0) {
668 error_fr(r, "sshkey_to_base64 failed");
669 goto out;
670 }
671
672 /* Turn the command into an argument vector */
673 if (argv_split(options.authorized_keys_command, &ac, &av, 0) != 0) {
674 error("AuthorizedKeysCommand \"%s\" contains invalid quotes",
675 options.authorized_keys_command);
676 goto out;
677 }
678 if (ac == 0) {
679 error("AuthorizedKeysCommand \"%s\" yielded no arguments",
680 options.authorized_keys_command);
681 goto out;
682 }
683 snprintf(uidstr, sizeof(uidstr), "%llu",
684 (unsigned long long)user_pw->pw_uid);
685 for (i = 1; i < ac; i++) {
686 tmp = percent_expand(av[i],
687 "C", conn_id,
688 "D", rdomain,
689 "U", uidstr,
690 "u", user_pw->pw_name,
691 "h", user_pw->pw_dir,
692 "t", sshkey_ssh_name(key),
693 "f", key_fp,
694 "k", keytext,
695 (char *)NULL);
696 if (tmp == NULL)
697 fatal_f("percent_expand failed");
698 free(av[i]);
699 av[i] = tmp;
700 }
701 /* Prepare a printable command for logs, etc. */
702 command = argv_assemble(ac, av);
703
704 /*
705 * If AuthorizedKeysCommand was run without arguments
706 * then fall back to the old behaviour of passing the
707 * target username as a single argument.
708 */
709 if (ac == 1) {
710 av = xreallocarray(av, ac + 2, sizeof(*av));
711 av[1] = xstrdup(user_pw->pw_name);
712 av[2] = NULL;
713 /* Fix up command too, since it is used in log messages */
714 free(command);
715 xasprintf(&command, "%s %s", av[0], av[1]);
716 }
717
718 if ((pid = subprocess("AuthorizedKeysCommand", command,
719 ac, av, &f,
720 SSH_SUBPROCESS_STDOUT_CAPTURE|SSH_SUBPROCESS_STDERR_DISCARD,
721 runas_pw, temporarily_use_uid, restore_uid)) == 0)
722 goto out;
723
724 uid_swapped = 1;
725 temporarily_use_uid(runas_pw);
726
727 ok = auth_check_authkeys_file(user_pw, f,
728 options.authorized_keys_command, key, remote_ip,
729 remote_host, authoptsp);
730
731 fclose(f);
732 f = NULL;
733
734 if (exited_cleanly(pid, "AuthorizedKeysCommand", command, 0) != 0)
735 goto out;
736
737 /* Read completed successfully */
738 found_key = ok;
739 out:
740 if (f != NULL)
741 fclose(f);
742 ssh_signal(SIGCHLD, osigchld);
743 for (i = 0; i < ac; i++)
744 free(av[i]);
745 free(av);
746 if (uid_swapped)
747 restore_uid();
748 free(command);
749 free(username);
750 free(key_fp);
751 free(keytext);
752 return found_key;
753 }
754
755 /*
756 * Check whether key authenticates and authorises the user.
757 */
758 int
user_key_allowed(struct ssh * ssh,struct passwd * pw,struct sshkey * key,int auth_attempt,struct sshauthopt ** authoptsp)759 user_key_allowed(struct ssh *ssh, struct passwd *pw, struct sshkey *key,
760 int auth_attempt, struct sshauthopt **authoptsp)
761 {
762 u_int success = 0, i;
763 char *file, *conn_id;
764 struct sshauthopt *opts = NULL;
765 const char *rdomain, *remote_ip, *remote_host;
766
767 if (authoptsp != NULL)
768 *authoptsp = NULL;
769
770 if (auth_key_is_revoked(key))
771 return 0;
772 if (sshkey_is_cert(key) &&
773 auth_key_is_revoked(key->cert->signature_key))
774 return 0;
775
776 if ((rdomain = ssh_packet_rdomain_in(ssh)) == NULL)
777 rdomain = "";
778 remote_ip = ssh_remote_ipaddr(ssh);
779 remote_host = auth_get_canonical_hostname(ssh, options.use_dns);
780 xasprintf(&conn_id, "%s %d %s %d",
781 ssh_local_ipaddr(ssh), ssh_local_port(ssh),
782 remote_ip, ssh_remote_port(ssh));
783
784 for (i = 0; !success && i < options.num_authkeys_files; i++) {
785 if (strcasecmp(options.authorized_keys_files[i], "none") == 0)
786 continue;
787 file = expand_authorized_keys(
788 options.authorized_keys_files[i], pw);
789 success = user_key_allowed2(pw, key, file,
790 remote_ip, remote_host, &opts);
791 free(file);
792 if (!success) {
793 sshauthopt_free(opts);
794 opts = NULL;
795 }
796 }
797 if (success)
798 goto out;
799
800 if ((success = user_cert_trusted_ca(pw, key, remote_ip, remote_host,
801 conn_id, rdomain, &opts)) != 0)
802 goto out;
803 sshauthopt_free(opts);
804 opts = NULL;
805
806 if ((success = user_key_command_allowed2(pw, key, remote_ip,
807 remote_host, conn_id, rdomain, &opts)) != 0)
808 goto out;
809 sshauthopt_free(opts);
810 opts = NULL;
811
812 out:
813 free(conn_id);
814 if (success && authoptsp != NULL) {
815 *authoptsp = opts;
816 opts = NULL;
817 }
818 sshauthopt_free(opts);
819 return success;
820 }
821
822 Authmethod method_pubkey = {
823 &methodcfg_pubkey,
824 userauth_pubkey,
825 };
826