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