blob: 8024b1d6a976092dcf526b705c0bb9b14649a8d5 [file] [log] [blame]
djm@openbsd.org7c856852018-03-03 03:15:51 +00001/* $OpenBSD: auth2-pubkey.c,v 1.77 2018/03/03 03:15:51 djm Exp $ */
Ben Lindstrom855bf3a2002-06-06 20:27:55 +00002/*
3 * Copyright (c) 2000 Markus Friedl. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "includes.h"
Damien Millerf17883e2006-03-15 11:45:54 +110027
28#include <sys/types.h>
29#include <sys/stat.h>
Ben Lindstrom855bf3a2002-06-06 20:27:55 +000030
Damien Miller09d3e122012-10-31 08:58:58 +110031#include <errno.h>
Darren Tucker06db5842008-06-13 14:51:28 +100032#include <fcntl.h>
Darren Tucker737f7af2012-11-05 17:07:43 +110033#ifdef HAVE_PATHS_H
34# include <paths.h>
35#endif
Damien Miller9f2abc42006-07-10 20:53:08 +100036#include <pwd.h>
Damien Miller09d3e122012-10-31 08:58:58 +110037#include <signal.h>
Damien Millera7a73ee2006-08-05 11:37:59 +100038#include <stdio.h>
Damien Millerd7834352006-08-05 12:39:39 +100039#include <stdarg.h>
Damien Miller0a80ca12010-02-27 07:55:05 +110040#include <string.h>
41#include <time.h>
Darren Tuckerd9526a52008-06-14 09:01:24 +100042#include <unistd.h>
djm@openbsd.orgf69b69b2014-12-22 07:51:30 +000043#include <limits.h>
Damien Miller9f2abc42006-07-10 20:53:08 +100044
Damien Millerd7834352006-08-05 12:39:39 +100045#include "xmalloc.h"
Darren Tucker22cc7412004-12-06 22:47:41 +110046#include "ssh.h"
Ben Lindstrom855bf3a2002-06-06 20:27:55 +000047#include "ssh2.h"
Ben Lindstrom855bf3a2002-06-06 20:27:55 +000048#include "packet.h"
49#include "buffer.h"
50#include "log.h"
Damien Miller7acefbb2014-07-18 14:11:24 +100051#include "misc.h"
Ben Lindstrom855bf3a2002-06-06 20:27:55 +000052#include "servconf.h"
53#include "compat.h"
markus@openbsd.org00ed75c2017-05-30 14:10:53 +000054#include "sshkey.h"
Damien Millerd7834352006-08-05 12:39:39 +100055#include "hostfile.h"
56#include "auth.h"
Ben Lindstrom855bf3a2002-06-06 20:27:55 +000057#include "pathnames.h"
58#include "uidswap.h"
59#include "auth-options.h"
60#include "canohost.h"
Damien Millerd7834352006-08-05 12:39:39 +100061#ifdef GSSAPI
62#include "ssh-gss.h"
63#endif
Ben Lindstrom855bf3a2002-06-06 20:27:55 +000064#include "monitor_wrap.h"
Damien Miller1aed65e2010-03-04 21:53:35 +110065#include "authfile.h"
Damien Miller30da3442010-05-10 11:58:03 +100066#include "match.h"
djm@openbsd.org24232a32015-05-21 06:38:35 +000067#include "ssherr.h"
68#include "channels.h" /* XXX for session.h */
69#include "session.h" /* XXX for child_set_env(); refactor? */
Ben Lindstrom855bf3a2002-06-06 20:27:55 +000070
71/* import */
72extern ServerOptions options;
73extern u_char *session_id2;
Darren Tucker502d3842003-06-28 12:38:01 +100074extern u_int session_id2_len;
Ben Lindstrom855bf3a2002-06-06 20:27:55 +000075
djm@openbsd.org27885632017-12-19 00:24:34 +000076static char *
77format_key(const struct sshkey *key)
78{
79 char *ret, *fp = sshkey_fingerprint(key,
80 options.fingerprint_hash, SSH_FP_DEFAULT);
81
82 xasprintf(&ret, "%s %s", sshkey_type(key), fp);
83 free(fp);
84 return ret;
85}
86
Ben Lindstrom855bf3a2002-06-06 20:27:55 +000087static int
markus@openbsd.orgeb272ea2017-05-30 14:29:59 +000088userauth_pubkey(struct ssh *ssh)
Ben Lindstrom855bf3a2002-06-06 20:27:55 +000089{
markus@openbsd.orgeb272ea2017-05-30 14:29:59 +000090 Authctxt *authctxt = ssh->authctxt;
djm@openbsd.org7c856852018-03-03 03:15:51 +000091 struct passwd *pw = authctxt->pw;
markus@openbsd.org00ed75c2017-05-30 14:10:53 +000092 struct sshbuf *b;
markus@openbsd.org54d90ac2017-05-30 08:52:19 +000093 struct sshkey *key = NULL;
djm@openbsd.org27885632017-12-19 00:24:34 +000094 char *pkalg, *userstyle = NULL, *key_s = NULL, *ca_s = NULL;
markus@openbsd.org00ed75c2017-05-30 14:10:53 +000095 u_char *pkblob, *sig, have_sig;
96 size_t blen, slen;
97 int r, pktype;
Ben Lindstrom855bf3a2002-06-06 20:27:55 +000098 int authenticated = 0;
djm@openbsd.org7c856852018-03-03 03:15:51 +000099 struct sshauthopt *authopts = NULL;
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000100
101 if (!authctxt->valid) {
djm@openbsd.orgebacd372016-01-27 00:53:12 +0000102 debug2("%s: disabled because of invalid user", __func__);
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000103 return 0;
104 }
djm@openbsd.org14b5c632018-01-23 05:27:21 +0000105 if ((r = sshpkt_get_u8(ssh, &have_sig)) != 0 ||
106 (r = sshpkt_get_cstring(ssh, &pkalg, NULL)) != 0 ||
107 (r = sshpkt_get_string(ssh, &pkblob, &blen)) != 0)
108 fatal("%s: parse request failed: %s", __func__, ssh_err(r));
markus@openbsd.org00ed75c2017-05-30 14:10:53 +0000109 pktype = sshkey_type_from_name(pkalg);
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000110 if (pktype == KEY_UNSPEC) {
111 /* this is perfectly legal */
djm@openbsd.orgebacd372016-01-27 00:53:12 +0000112 logit("%s: unsupported public key algorithm: %s",
113 __func__, pkalg);
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000114 goto done;
115 }
markus@openbsd.org00ed75c2017-05-30 14:10:53 +0000116 if ((r = sshkey_from_blob(pkblob, blen, &key)) != 0) {
117 error("%s: could not parse key: %s", __func__, ssh_err(r));
118 goto done;
119 }
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000120 if (key == NULL) {
djm@openbsd.orgebacd372016-01-27 00:53:12 +0000121 error("%s: cannot decode key: %s", __func__, pkalg);
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000122 goto done;
123 }
124 if (key->type != pktype) {
djm@openbsd.orgebacd372016-01-27 00:53:12 +0000125 error("%s: type mismatch for decoded key "
126 "(received %d, expected %d)", __func__, key->type, pktype);
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000127 goto done;
128 }
markus@openbsd.org00ed75c2017-05-30 14:10:53 +0000129 if (sshkey_type_plain(key->type) == KEY_RSA &&
130 (ssh->compat & SSH_BUG_RSASIGMD5) != 0) {
Damien Miller324541e2013-12-31 12:25:40 +1100131 logit("Refusing RSA key because client uses unsafe "
132 "signature scheme");
133 goto done;
134 }
djm@openbsd.org8f574952017-06-24 06:34:38 +0000135 if (auth2_key_already_used(authctxt, key)) {
markus@openbsd.org00ed75c2017-05-30 14:10:53 +0000136 logit("refusing previously-used %s key", sshkey_type(key));
djm@openbsd.orgf69b69b2014-12-22 07:51:30 +0000137 goto done;
138 }
djm@openbsd.orge661a862015-05-04 06:10:48 +0000139 if (match_pattern_list(sshkey_ssh_name(key),
140 options.pubkey_key_types, 0) != 1) {
djm@openbsd.org1f729f02015-01-13 07:39:19 +0000141 logit("%s: key type %s not in PubkeyAcceptedKeyTypes",
142 __func__, sshkey_ssh_name(key));
143 goto done;
144 }
145
djm@openbsd.org27885632017-12-19 00:24:34 +0000146 key_s = format_key(key);
147 if (sshkey_is_cert(key))
148 ca_s = format_key(key->cert->signature_key);
149
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000150 if (have_sig) {
djm@openbsd.org27885632017-12-19 00:24:34 +0000151 debug3("%s: have %s signature for %s%s%s",
152 __func__, pkalg, key_s,
153 ca_s == NULL ? "" : " CA ",
154 ca_s == NULL ? "" : ca_s);
markus@openbsd.org00ed75c2017-05-30 14:10:53 +0000155 if ((r = sshpkt_get_string(ssh, &sig, &slen)) != 0 ||
156 (r = sshpkt_get_end(ssh)) != 0)
157 fatal("%s: %s", __func__, ssh_err(r));
158 if ((b = sshbuf_new()) == NULL)
159 fatal("%s: sshbuf_new failed", __func__);
160 if (ssh->compat & SSH_OLD_SESSIONID) {
161 if ((r = sshbuf_put(b, session_id2,
162 session_id2_len)) != 0)
163 fatal("%s: sshbuf_put session id: %s",
164 __func__, ssh_err(r));
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000165 } else {
markus@openbsd.org00ed75c2017-05-30 14:10:53 +0000166 if ((r = sshbuf_put_string(b, session_id2,
167 session_id2_len)) != 0)
168 fatal("%s: sshbuf_put_string session id: %s",
169 __func__, ssh_err(r));
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000170 }
171 /* reconstruct packet */
Damien Miller4ce189d2013-04-23 15:17:52 +1000172 xasprintf(&userstyle, "%s%s%s", authctxt->user,
173 authctxt->style ? ":" : "",
174 authctxt->style ? authctxt->style : "");
markus@openbsd.org00ed75c2017-05-30 14:10:53 +0000175 if ((r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
176 (r = sshbuf_put_cstring(b, userstyle)) != 0 ||
djm@openbsd.org14b5c632018-01-23 05:27:21 +0000177 (r = sshbuf_put_cstring(b, authctxt->service)) != 0 ||
178 (r = sshbuf_put_cstring(b, "publickey")) != 0 ||
179 (r = sshbuf_put_u8(b, have_sig)) != 0 ||
180 (r = sshbuf_put_cstring(b, pkalg) != 0) ||
181 (r = sshbuf_put_string(b, pkblob, blen)) != 0)
markus@openbsd.org00ed75c2017-05-30 14:10:53 +0000182 fatal("%s: build packet failed: %s",
183 __func__, ssh_err(r));
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000184#ifdef DEBUG_PK
markus@openbsd.org00ed75c2017-05-30 14:10:53 +0000185 sshbuf_dump(b, stderr);
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000186#endif
Darren Tucker74836ae2013-06-02 07:32:00 +1000187
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000188 /* test for correct signature */
189 authenticated = 0;
djm@openbsd.org7c856852018-03-03 03:15:51 +0000190 if (PRIVSEP(user_key_allowed(ssh, pw, key, 1, &authopts)) &&
markus@openbsd.org00ed75c2017-05-30 14:10:53 +0000191 PRIVSEP(sshkey_verify(key, sig, slen, sshbuf_ptr(b),
djm@openbsd.orgd45d69f2017-12-21 00:00:28 +0000192 sshbuf_len(b), NULL, ssh->compat)) == 0) {
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000193 authenticated = 1;
djm@openbsd.orgf69b69b2014-12-22 07:51:30 +0000194 }
markus@openbsd.org00ed75c2017-05-30 14:10:53 +0000195 sshbuf_free(b);
Darren Tuckera627d422013-06-02 07:31:17 +1000196 free(sig);
djm@openbsd.org8f574952017-06-24 06:34:38 +0000197 auth2_record_key(authctxt, authenticated, key);
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000198 } else {
djm@openbsd.org27885632017-12-19 00:24:34 +0000199 debug("%s: test pkalg %s pkblob %s%s%s",
200 __func__, pkalg, key_s,
201 ca_s == NULL ? "" : " CA ",
202 ca_s == NULL ? "" : ca_s);
203
markus@openbsd.org00ed75c2017-05-30 14:10:53 +0000204 if ((r = sshpkt_get_end(ssh)) != 0)
205 fatal("%s: %s", __func__, ssh_err(r));
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000206
207 /* XXX fake reply and always send PK_OK ? */
208 /*
209 * XXX this allows testing whether a user is allowed
210 * to login: if you happen to have a valid pubkey this
211 * message is sent. the message is NEVER sent at all
212 * if a user is not allowed to login. is this an
213 * issue? -markus
214 */
djm@openbsd.org7c856852018-03-03 03:15:51 +0000215 if (PRIVSEP(user_key_allowed(ssh, pw, key, 0, NULL))) {
markus@openbsd.org00ed75c2017-05-30 14:10:53 +0000216 if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_PK_OK))
217 != 0 ||
218 (r = sshpkt_put_cstring(ssh, pkalg)) != 0 ||
219 (r = sshpkt_put_string(ssh, pkblob, blen)) != 0 ||
220 (r = sshpkt_send(ssh)) != 0)
221 fatal("%s: %s", __func__, ssh_err(r));
222 ssh_packet_write_wait(ssh);
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000223 authctxt->postponed = 1;
224 }
225 }
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000226done:
djm@openbsd.org7c856852018-03-03 03:15:51 +0000227 if (authenticated == 1 && auth_activate_options(ssh, authopts) != 0) {
228 debug("%s: key options inconsistent with existing", __func__);
229 authenticated = 0;
230 }
djm@openbsd.orgebacd372016-01-27 00:53:12 +0000231 debug2("%s: authenticated %d pkalg %s", __func__, authenticated, pkalg);
djm@openbsd.org7c856852018-03-03 03:15:51 +0000232
233 sshauthopt_free(authopts);
djm@openbsd.org8f574952017-06-24 06:34:38 +0000234 sshkey_free(key);
markus@openbsd.org00ed75c2017-05-30 14:10:53 +0000235 free(userstyle);
Darren Tuckera627d422013-06-02 07:31:17 +1000236 free(pkalg);
237 free(pkblob);
djm@openbsd.org27885632017-12-19 00:24:34 +0000238 free(key_s);
239 free(ca_s);
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000240 return authenticated;
241}
242
Damien Miller30da3442010-05-10 11:58:03 +1000243static int
Damien Miller86687062014-07-02 15:28:02 +1000244match_principals_option(const char *principal_list, struct sshkey_cert *cert)
Damien Miller30da3442010-05-10 11:58:03 +1000245{
246 char *result;
247 u_int i;
248
249 /* XXX percent_expand() sequences for authorized_principals? */
250
251 for (i = 0; i < cert->nprincipals; i++) {
252 if ((result = match_list(cert->principals[i],
253 principal_list, NULL)) != NULL) {
254 debug3("matched principal from key options \"%.100s\"",
255 result);
Darren Tuckera627d422013-06-02 07:31:17 +1000256 free(result);
Damien Miller30da3442010-05-10 11:58:03 +1000257 return 1;
258 }
259 }
260 return 0;
261}
262
djm@openbsd.org7c856852018-03-03 03:15:51 +0000263/*
264 * Process a single authorized_principals format line. Returns 0 and sets
265 * authoptsp is principal is authorised, -1 otherwise. "loc" is used as a
266 * log preamble for file/line information.
267 */
Damien Miller30da3442010-05-10 11:58:03 +1000268static int
djm@openbsd.org7c856852018-03-03 03:15:51 +0000269check_principals_line(struct ssh *ssh, char *cp, const struct sshkey_cert *cert,
270 const char *loc, struct sshauthopt **authoptsp)
Damien Miller30da3442010-05-10 11:58:03 +1000271{
djm@openbsd.org7c856852018-03-03 03:15:51 +0000272 u_int i, found = 0;
273 char *ep, *line_opts;
274 const char *reason = NULL;
275 struct sshauthopt *opts = NULL;
276
277 if (authoptsp != NULL)
278 *authoptsp = NULL;
279
280 /* Trim trailing whitespace. */
281 ep = cp + strlen(cp) - 1;
282 while (ep > cp && (*ep == '\n' || *ep == ' ' || *ep == '\t'))
283 *ep-- = '\0';
284
285 /*
286 * If the line has internal whitespace then assume it has
287 * key options.
288 */
289 line_opts = NULL;
290 if ((ep = strrchr(cp, ' ')) != NULL ||
291 (ep = strrchr(cp, '\t')) != NULL) {
292 for (; *ep == ' ' || *ep == '\t'; ep++)
293 ;
294 line_opts = cp;
295 cp = ep;
296 }
297 if ((opts = sshauthopt_parse(line_opts, &reason)) == NULL) {
298 debug("%s: bad principals options: %s", loc, reason);
299 auth_debug_add("%s: bad principals options: %s", loc, reason);
300 return -1;
301 }
302 /* Check principals in cert against those on line */
303 for (i = 0; i < cert->nprincipals; i++) {
304 if (strcmp(cp, cert->principals[i]) != 0)
305 continue;
306 debug3("%s: matched principal \"%.100s\"",
307 loc, cert->principals[i]);
308 found = 1;
309 }
310 if (found && authoptsp != NULL) {
311 *authoptsp = opts;
312 opts = NULL;
313 }
314 sshauthopt_free(opts);
315 return found ? 0 : -1;
316}
317
318static int
319process_principals(struct ssh *ssh, FILE *f, const char *file,
320 const struct sshkey_cert *cert, struct sshauthopt **authoptsp)
321{
322 char loc[256], line[SSH_MAX_PUBKEY_BYTES], *cp, *ep;
Damien Miller30da3442010-05-10 11:58:03 +1000323 u_long linenum = 0;
djm@openbsd.org7c856852018-03-03 03:15:51 +0000324 u_int found_principal = 0;
325
326 if (authoptsp != NULL)
327 *authoptsp = NULL;
Damien Miller30da3442010-05-10 11:58:03 +1000328
Damien Miller30da3442010-05-10 11:58:03 +1000329 while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) {
djm@openbsd.org52763dd2017-01-30 01:03:00 +0000330 /* Always consume entire input */
331 if (found_principal)
332 continue;
djm@openbsd.org7c856852018-03-03 03:15:51 +0000333
Damien Miller6018a362010-07-02 13:35:19 +1000334 /* Skip leading whitespace. */
Damien Miller30da3442010-05-10 11:58:03 +1000335 for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
336 ;
Damien Miller6018a362010-07-02 13:35:19 +1000337 /* Skip blank and comment lines. */
338 if ((ep = strchr(cp, '#')) != NULL)
339 *ep = '\0';
340 if (!*cp || *cp == '\n')
Damien Miller30da3442010-05-10 11:58:03 +1000341 continue;
djm@openbsd.org7c856852018-03-03 03:15:51 +0000342
343 snprintf(loc, sizeof(loc), "%.200s:%lu", file, linenum);
344 if (check_principals_line(ssh, cp, cert, loc, authoptsp) == 0)
345 found_principal = 1;
Damien Miller30da3442010-05-10 11:58:03 +1000346 }
djm@openbsd.org52763dd2017-01-30 01:03:00 +0000347 return found_principal;
Damien Miller09d3e122012-10-31 08:58:58 +1100348}
Damien Miller30da3442010-05-10 11:58:03 +1000349
djm@openbsd.org7c856852018-03-03 03:15:51 +0000350/* XXX remove pw args here and elsewhere once ssh->authctxt is guaranteed */
351
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000352static int
djm@openbsd.org7c856852018-03-03 03:15:51 +0000353match_principals_file(struct ssh *ssh, struct passwd *pw, char *file,
354 struct sshkey_cert *cert, struct sshauthopt **authoptsp)
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000355{
356 FILE *f;
357 int success;
358
djm@openbsd.org7c856852018-03-03 03:15:51 +0000359 if (authoptsp != NULL)
360 *authoptsp = NULL;
361
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000362 temporarily_use_uid(pw);
363 debug("trying authorized principals file %s", file);
364 if ((f = auth_openprincipals(file, pw, options.strict_modes)) == NULL) {
365 restore_uid();
366 return 0;
367 }
djm@openbsd.org7c856852018-03-03 03:15:51 +0000368 success = process_principals(ssh, f, file, cert, authoptsp);
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000369 fclose(f);
370 restore_uid();
371 return success;
372}
373
374/*
375 * Checks whether principal is allowed in output of command.
376 * returns 1 if the principal is allowed or 0 otherwise.
377 */
378static int
djm@openbsd.org7c856852018-03-03 03:15:51 +0000379match_principals_command(struct ssh *ssh, struct passwd *user_pw,
380 const struct sshkey *key, struct sshauthopt **authoptsp)
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000381{
djm@openbsd.org7c856852018-03-03 03:15:51 +0000382 struct passwd *runas_pw = NULL;
djm@openbsd.orge7907c12016-09-14 05:42:25 +0000383 const struct sshkey_cert *cert = key->cert;
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000384 FILE *f = NULL;
djm@openbsd.orge7907c12016-09-14 05:42:25 +0000385 int r, ok, found_principal = 0;
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000386 int i, ac = 0, uid_swapped = 0;
387 pid_t pid;
388 char *tmp, *username = NULL, *command = NULL, **av = NULL;
djm@openbsd.orge7907c12016-09-14 05:42:25 +0000389 char *ca_fp = NULL, *key_fp = NULL, *catext = NULL, *keytext = NULL;
djm@openbsd.orgbfa9d962016-09-21 01:34:45 +0000390 char serial_s[16];
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000391 void (*osigchld)(int);
392
djm@openbsd.org7c856852018-03-03 03:15:51 +0000393 if (authoptsp != NULL)
394 *authoptsp = NULL;
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000395 if (options.authorized_principals_command == NULL)
396 return 0;
397 if (options.authorized_principals_command_user == NULL) {
398 error("No user for AuthorizedPrincipalsCommand specified, "
399 "skipping");
400 return 0;
401 }
402
403 /*
404 * NB. all returns later this function should go via "out" to
405 * ensure the original SIGCHLD handler is restored properly.
406 */
407 osigchld = signal(SIGCHLD, SIG_DFL);
408
409 /* Prepare and verify the user for the command */
410 username = percent_expand(options.authorized_principals_command_user,
411 "u", user_pw->pw_name, (char *)NULL);
djm@openbsd.org7c856852018-03-03 03:15:51 +0000412 runas_pw = getpwnam(username);
413 if (runas_pw == NULL) {
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000414 error("AuthorizedPrincipalsCommandUser \"%s\" not found: %s",
415 username, strerror(errno));
416 goto out;
417 }
418
419 /* Turn the command into an argument vector */
djm@openbsd.orgde4ae072017-08-18 05:36:45 +0000420 if (argv_split(options.authorized_principals_command, &ac, &av) != 0) {
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000421 error("AuthorizedPrincipalsCommand \"%s\" contains "
422 "invalid quotes", command);
423 goto out;
424 }
425 if (ac == 0) {
426 error("AuthorizedPrincipalsCommand \"%s\" yielded no arguments",
427 command);
428 goto out;
429 }
djm@openbsd.orge7907c12016-09-14 05:42:25 +0000430 if ((ca_fp = sshkey_fingerprint(cert->signature_key,
431 options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) {
432 error("%s: sshkey_fingerprint failed", __func__);
433 goto out;
434 }
djm@openbsd.org00df97f2016-09-14 20:11:26 +0000435 if ((key_fp = sshkey_fingerprint(key,
djm@openbsd.orge7907c12016-09-14 05:42:25 +0000436 options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) {
437 error("%s: sshkey_fingerprint failed", __func__);
438 goto out;
439 }
440 if ((r = sshkey_to_base64(cert->signature_key, &catext)) != 0) {
441 error("%s: sshkey_to_base64 failed: %s", __func__, ssh_err(r));
442 goto out;
443 }
444 if ((r = sshkey_to_base64(key, &keytext)) != 0) {
445 error("%s: sshkey_to_base64 failed: %s", __func__, ssh_err(r));
446 goto out;
447 }
djm@openbsd.orgf83a0cf2016-09-21 17:44:20 +0000448 snprintf(serial_s, sizeof(serial_s), "%llu",
449 (unsigned long long)cert->serial);
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000450 for (i = 1; i < ac; i++) {
451 tmp = percent_expand(av[i],
452 "u", user_pw->pw_name,
453 "h", user_pw->pw_dir,
djm@openbsd.orge7907c12016-09-14 05:42:25 +0000454 "t", sshkey_ssh_name(key),
455 "T", sshkey_ssh_name(cert->signature_key),
456 "f", key_fp,
457 "F", ca_fp,
458 "k", keytext,
459 "K", catext,
djm@openbsd.orgbfa9d962016-09-21 01:34:45 +0000460 "i", cert->key_id,
461 "s", serial_s,
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000462 (char *)NULL);
463 if (tmp == NULL)
464 fatal("%s: percent_expand failed", __func__);
465 free(av[i]);
466 av[i] = tmp;
467 }
468 /* Prepare a printable command for logs, etc. */
djm@openbsd.orgde4ae072017-08-18 05:36:45 +0000469 command = argv_assemble(ac, av);
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000470
djm@openbsd.org7c856852018-03-03 03:15:51 +0000471 if ((pid = subprocess("AuthorizedPrincipalsCommand", runas_pw, command,
djm@openbsd.orgde4ae072017-08-18 05:36:45 +0000472 ac, av, &f,
473 SSH_SUBPROCESS_STDOUT_CAPTURE|SSH_SUBPROCESS_STDERR_DISCARD)) == 0)
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000474 goto out;
475
476 uid_swapped = 1;
djm@openbsd.org7c856852018-03-03 03:15:51 +0000477 temporarily_use_uid(runas_pw);
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000478
djm@openbsd.org7c856852018-03-03 03:15:51 +0000479 ok = process_principals(ssh, f, "(command)", cert, authoptsp);
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000480
djm@openbsd.orgddd3d342016-12-30 22:08:02 +0000481 fclose(f);
482 f = NULL;
483
djm@openbsd.orgb074c3c2017-08-18 05:48:04 +0000484 if (exited_cleanly(pid, "AuthorizedPrincipalsCommand", command, 0) != 0)
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000485 goto out;
486
487 /* Read completed successfully */
488 found_principal = ok;
489 out:
490 if (f != NULL)
491 fclose(f);
492 signal(SIGCHLD, osigchld);
493 for (i = 0; i < ac; i++)
494 free(av[i]);
495 free(av);
496 if (uid_swapped)
497 restore_uid();
498 free(command);
499 free(username);
djm@openbsd.orge7907c12016-09-14 05:42:25 +0000500 free(ca_fp);
501 free(key_fp);
502 free(catext);
503 free(keytext);
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000504 return found_principal;
505}
djm@openbsd.org7c856852018-03-03 03:15:51 +0000506
507static void
508skip_space(char **cpp)
509{
510 char *cp;
511
512 for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++)
513 ;
514 *cpp = cp;
515}
516
517/*
518 * Advanced *cpp past the end of key options, defined as the first unquoted
519 * whitespace character. Returns 0 on success or -1 on failure (e.g.
520 * unterminated quotes).
521 */
522static int
523advance_past_options(char **cpp)
524{
525 char *cp = *cpp;
526 int quoted = 0;
527
528 for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
529 if (*cp == '\\' && cp[1] == '"')
530 cp++; /* Skip both */
531 else if (*cp == '"')
532 quoted = !quoted;
533 }
534 *cpp = cp;
535 /* return failure for unterminated quotes */
536 return (*cp == '\0' && quoted) ? -1 : 0;
537}
538
539/*
540 * Check a single line of an authorized_keys-format file. Returns 0 if key
541 * matches, -1 otherwise. Will return key/cert options via *authoptsp
542 * on success. "loc" is used as file/line location in log messages.
543 */
544static int
545check_authkey_line(struct ssh *ssh, struct passwd *pw, struct sshkey *key,
546 char *cp, const char *loc, struct sshauthopt **authoptsp)
547{
548 int want_keytype = sshkey_is_cert(key) ? KEY_UNSPEC : key->type;
549 struct sshkey *found = NULL;
550 struct sshauthopt *keyopts = NULL, *certopts = NULL, *finalopts = NULL;
551 char *key_options = NULL, *fp = NULL;
552 const char *reason = NULL;
553 int ret = -1;
554
555 if (authoptsp != NULL)
556 *authoptsp = NULL;
557
558 if ((found = sshkey_new(want_keytype)) == NULL) {
559 debug3("%s: keytype %d failed", __func__, want_keytype);
560 goto out;
561 }
562
563 /* XXX djm: peek at key type in line and skip if unwanted */
564
565 if (sshkey_read(found, &cp) != 0) {
566 /* no key? check for options */
567 debug2("%s: check options: '%s'", loc, cp);
568 key_options = cp;
569 if (advance_past_options(&cp) != 0) {
570 reason = "invalid key option string";
571 goto fail_reason;
572 }
573 skip_space(&cp);
574 if (sshkey_read(found, &cp) != 0) {
575 /* still no key? advance to next line*/
576 debug2("%s: advance: '%s'", loc, cp);
577 goto out;
578 }
579 }
580 /* Parse key options now; we need to know if this is a CA key */
581 if ((keyopts = sshauthopt_parse(key_options, &reason)) == NULL) {
582 debug("%s: bad key options: %s", loc, reason);
583 auth_debug_add("%s: bad key options: %s", loc, reason);
584 goto out;
585 }
586 /* Ignore keys that don't match or incorrectly marked as CAs */
587 if (sshkey_is_cert(key)) {
588 /* Certificate; check signature key against CA */
589 if (!sshkey_equal(found, key->cert->signature_key) ||
590 !keyopts->cert_authority)
591 goto out;
592 } else {
593 /* Plain key: check it against key found in file */
594 if (!sshkey_equal(found, key) || keyopts->cert_authority)
595 goto out;
596 }
597
598 /* We have a candidate key, perform authorisation checks */
599 if ((fp = sshkey_fingerprint(found,
600 options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
601 fatal("%s: fingerprint failed", __func__);
602
603 debug("%s: matching %s found: %s %s", loc,
604 sshkey_is_cert(key) ? "CA" : "key", sshkey_type(found), fp);
605
606 if (auth_authorise_keyopts(ssh, pw, keyopts,
607 sshkey_is_cert(key), loc) != 0) {
608 reason = "Refused by key options";
609 goto fail_reason;
610 }
611 /* That's all we need for plain keys. */
612 if (!sshkey_is_cert(key)) {
613 verbose("Accepted key %s %s found at %s",
614 sshkey_type(found), fp, loc);
615 finalopts = keyopts;
616 keyopts = NULL;
617 goto success;
618 }
619
620 /*
621 * Additional authorisation for certificates.
622 */
623
624 /* Parse and check options present in certificate */
625 if ((certopts = sshauthopt_from_cert(key)) == NULL) {
626 reason = "Invalid certificate options";
627 goto fail_reason;
628 }
629 if (auth_authorise_keyopts(ssh, pw, certopts, 0, loc) != 0) {
630 reason = "Refused by certificate options";
631 goto fail_reason;
632 }
633 if ((finalopts = sshauthopt_merge(keyopts, certopts, &reason)) == NULL)
634 goto fail_reason;
635
636 /*
637 * If the user has specified a list of principals as
638 * a key option, then prefer that list to matching
639 * their username in the certificate principals list.
640 */
641 if (keyopts->cert_principals != NULL &&
642 !match_principals_option(keyopts->cert_principals, key->cert)) {
643 reason = "Certificate does not contain an authorized principal";
644 goto fail_reason;
645 }
646 if (sshkey_cert_check_authority(key, 0, 0,
647 keyopts->cert_principals == NULL ? pw->pw_name : NULL, &reason) != 0)
648 goto fail_reason;
649
650 verbose("Accepted certificate ID \"%s\" (serial %llu) "
651 "signed by CA %s %s found at %s",
652 key->cert->key_id,
653 (unsigned long long)key->cert->serial,
654 sshkey_type(found), fp, loc);
655
656 success:
657 if (finalopts == NULL)
658 fatal("%s: internal error: missing options", __func__);
659 if (authoptsp != NULL) {
660 *authoptsp = finalopts;
661 finalopts = NULL;
662 }
663 /* success */
664 ret = 0;
665 goto out;
666
667 fail_reason:
668 error("%s", reason);
669 auth_debug_add("%s", reason);
670 out:
671 free(fp);
672 sshauthopt_free(keyopts);
673 sshauthopt_free(certopts);
674 sshauthopt_free(finalopts);
675 sshkey_free(found);
676 return ret;
677}
678
Damien Miller09d3e122012-10-31 08:58:58 +1100679/*
680 * Checks whether key is allowed in authorized_keys-format file,
681 * returns 1 if the key is allowed or 0 otherwise.
682 */
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000683static int
djm@openbsd.org7c856852018-03-03 03:15:51 +0000684check_authkeys_file(struct ssh *ssh, struct passwd *pw, FILE *f,
685 char *file, struct sshkey *key, struct sshauthopt **authoptsp)
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000686{
djm@openbsd.org7c856852018-03-03 03:15:51 +0000687 char *cp, line[SSH_MAX_PUBKEY_BYTES], loc[256];
Darren Tucker33c787f2008-07-02 22:37:30 +1000688 int found_key = 0;
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000689 u_long linenum = 0;
djm@openbsd.org7c856852018-03-03 03:15:51 +0000690
691 if (authoptsp != NULL)
692 *authoptsp = NULL;
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000693
Darren Tucker22cc7412004-12-06 22:47:41 +1100694 while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) {
djm@openbsd.orgabd59662017-09-07 23:48:09 +0000695 /* Always consume entire file */
djm@openbsd.org52763dd2017-01-30 01:03:00 +0000696 if (found_key)
697 continue;
Damien Miller0a80ca12010-02-27 07:55:05 +1100698
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000699 /* Skip leading whitespace, empty and comment lines. */
djm@openbsd.org7c856852018-03-03 03:15:51 +0000700 cp = line;
701 skip_space(&cp);
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000702 if (!*cp || *cp == '\n' || *cp == '#')
703 continue;
djm@openbsd.org7c856852018-03-03 03:15:51 +0000704 snprintf(loc, sizeof(loc), "%.200s:%lu", file, linenum);
705 if (check_authkey_line(ssh, pw, key, cp, loc, authoptsp) == 0)
Damien Miller0a80ca12010-02-27 07:55:05 +1100706 found_key = 1;
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000707 }
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000708 return found_key;
709}
710
Damien Miller1aed65e2010-03-04 21:53:35 +1100711/* Authenticate a certificate key against TrustedUserCAKeys */
712static int
djm@openbsd.org7c856852018-03-03 03:15:51 +0000713user_cert_trusted_ca(struct ssh *ssh, struct passwd *pw, struct sshkey *key,
714 struct sshauthopt **authoptsp)
Damien Miller1aed65e2010-03-04 21:53:35 +1100715{
Damien Miller30da3442010-05-10 11:58:03 +1000716 char *ca_fp, *principals_file = NULL;
Damien Miller1aed65e2010-03-04 21:53:35 +1100717 const char *reason;
djm@openbsd.org7c856852018-03-03 03:15:51 +0000718 struct sshauthopt *principals_opts = NULL, *cert_opts = NULL;
719 struct sshauthopt *final_opts = NULL;
markus@openbsd.org00ed75c2017-05-30 14:10:53 +0000720 int r, ret = 0, found_principal = 0, use_authorized_principals;
Damien Miller1aed65e2010-03-04 21:53:35 +1100721
djm@openbsd.org7c856852018-03-03 03:15:51 +0000722 if (authoptsp != NULL)
723 *authoptsp = NULL;
724
markus@openbsd.org00ed75c2017-05-30 14:10:53 +0000725 if (!sshkey_is_cert(key) || options.trusted_user_ca_keys == NULL)
Damien Miller1aed65e2010-03-04 21:53:35 +1100726 return 0;
727
djm@openbsd.org9ce86c92015-01-28 22:36:00 +0000728 if ((ca_fp = sshkey_fingerprint(key->cert->signature_key,
729 options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
730 return 0;
Damien Miller1aed65e2010-03-04 21:53:35 +1100731
markus@openbsd.org00ed75c2017-05-30 14:10:53 +0000732 if ((r = sshkey_in_file(key->cert->signature_key,
733 options.trusted_user_ca_keys, 1, 0)) != 0) {
734 debug2("%s: CA %s %s is not listed in %s: %s", __func__,
735 sshkey_type(key->cert->signature_key), ca_fp,
736 options.trusted_user_ca_keys, ssh_err(r));
Damien Miller1aed65e2010-03-04 21:53:35 +1100737 goto out;
738 }
Damien Miller30da3442010-05-10 11:58:03 +1000739 /*
740 * If AuthorizedPrincipals is in use, then compare the certificate
741 * principals against the names in that file rather than matching
742 * against the username.
743 */
744 if ((principals_file = authorized_principals_file(pw)) != NULL) {
djm@openbsd.org7c856852018-03-03 03:15:51 +0000745 if (match_principals_file(ssh, pw, principals_file,
746 key->cert, &principals_opts))
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000747 found_principal = 1;
748 }
749 /* Try querying command if specified */
djm@openbsd.org7c856852018-03-03 03:15:51 +0000750 if (!found_principal && match_principals_command(ssh, pw, key,
751 &principals_opts))
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000752 found_principal = 1;
jsing@openbsd.org596dbca2015-06-15 18:44:22 +0000753 /* If principals file or command is specified, then require a match */
754 use_authorized_principals = principals_file != NULL ||
755 options.authorized_principals_command != NULL;
756 if (!found_principal && use_authorized_principals) {
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000757 reason = "Certificate does not contain an authorized principal";
djm@openbsd.org7c856852018-03-03 03:15:51 +0000758 goto fail_reason;
Damien Miller1aed65e2010-03-04 21:53:35 +1100759 }
djm@openbsd.org7c856852018-03-03 03:15:51 +0000760 if (use_authorized_principals && principals_opts == NULL)
761 fatal("%s: internal error: missing principals_opts", __func__);
markus@openbsd.org00ed75c2017-05-30 14:10:53 +0000762 if (sshkey_cert_check_authority(key, 0, 1,
jsing@openbsd.org596dbca2015-06-15 18:44:22 +0000763 use_authorized_principals ? NULL : pw->pw_name, &reason) != 0)
Damien Miller30da3442010-05-10 11:58:03 +1000764 goto fail_reason;
Damien Miller1aed65e2010-03-04 21:53:35 +1100765
djm@openbsd.org7c856852018-03-03 03:15:51 +0000766 /* Check authority from options in key and from principals file/cmd */
767 if ((cert_opts = sshauthopt_from_cert(key)) == NULL) {
768 reason = "Invalid certificate options";
769 goto fail_reason;
770 }
771 if (auth_authorise_keyopts(ssh, pw, cert_opts, 0, "cert") != 0) {
772 reason = "Refused by certificate options";
773 goto fail_reason;
774 }
775 if (principals_opts == NULL) {
776 final_opts = cert_opts;
777 cert_opts = NULL;
778 } else {
779 if (auth_authorise_keyopts(ssh, pw, principals_opts, 0,
780 "principals") != 0) {
781 reason = "Refused by certificate principals options";
782 goto fail_reason;
783 }
784 if ((final_opts = sshauthopt_merge(principals_opts,
785 cert_opts, &reason)) == NULL) {
786 fail_reason:
787 error("%s", reason);
788 auth_debug_add("%s", reason);
789 goto out;
790 }
791 }
792
793 /* Success */
djm@openbsd.org63d18812015-10-27 01:44:45 +0000794 verbose("Accepted certificate ID \"%s\" (serial %llu) signed by "
795 "%s CA %s via %s", key->cert->key_id,
796 (unsigned long long)key->cert->serial,
markus@openbsd.org00ed75c2017-05-30 14:10:53 +0000797 sshkey_type(key->cert->signature_key), ca_fp,
Damien Millere513a912010-03-22 05:51:21 +1100798 options.trusted_user_ca_keys);
djm@openbsd.org7c856852018-03-03 03:15:51 +0000799 if (authoptsp != NULL) {
800 *authoptsp = final_opts;
801 final_opts = NULL;
802 }
Damien Miller1aed65e2010-03-04 21:53:35 +1100803 ret = 1;
Damien Miller1aed65e2010-03-04 21:53:35 +1100804 out:
djm@openbsd.org7c856852018-03-03 03:15:51 +0000805 sshauthopt_free(principals_opts);
806 sshauthopt_free(cert_opts);
807 sshauthopt_free(final_opts);
Darren Tuckera627d422013-06-02 07:31:17 +1000808 free(principals_file);
809 free(ca_fp);
Damien Miller1aed65e2010-03-04 21:53:35 +1100810 return ret;
811}
812
Damien Miller09d3e122012-10-31 08:58:58 +1100813/*
814 * Checks whether key is allowed in file.
815 * returns 1 if the key is allowed or 0 otherwise.
816 */
817static int
djm@openbsd.org7c856852018-03-03 03:15:51 +0000818user_key_allowed2(struct ssh *ssh, struct passwd *pw, struct sshkey *key,
819 char *file, struct sshauthopt **authoptsp)
Damien Miller09d3e122012-10-31 08:58:58 +1100820{
821 FILE *f;
822 int found_key = 0;
823
djm@openbsd.org7c856852018-03-03 03:15:51 +0000824 if (authoptsp != NULL)
825 *authoptsp = NULL;
826
Damien Miller09d3e122012-10-31 08:58:58 +1100827 /* Temporarily use the user's uid. */
828 temporarily_use_uid(pw);
829
830 debug("trying public key file %s", file);
831 if ((f = auth_openkeyfile(file, pw, options.strict_modes)) != NULL) {
djm@openbsd.org7c856852018-03-03 03:15:51 +0000832 found_key = check_authkeys_file(ssh, pw, f, file,
833 key, authoptsp);
Damien Miller09d3e122012-10-31 08:58:58 +1100834 fclose(f);
835 }
836
837 restore_uid();
838 return found_key;
839}
840
841/*
842 * Checks whether key is allowed in output of command.
843 * returns 1 if the key is allowed or 0 otherwise.
844 */
845static int
djm@openbsd.org7c856852018-03-03 03:15:51 +0000846user_key_command_allowed2(struct ssh *ssh, struct passwd *user_pw,
847 struct sshkey *key, struct sshauthopt **authoptsp)
Damien Miller09d3e122012-10-31 08:58:58 +1100848{
djm@openbsd.org7c856852018-03-03 03:15:51 +0000849 struct passwd *runas_pw = NULL;
djm@openbsd.org24232a32015-05-21 06:38:35 +0000850 FILE *f = NULL;
851 int r, ok, found_key = 0;
djm@openbsd.org24232a32015-05-21 06:38:35 +0000852 int i, uid_swapped = 0, ac = 0;
Damien Miller09d3e122012-10-31 08:58:58 +1100853 pid_t pid;
djm@openbsd.org24232a32015-05-21 06:38:35 +0000854 char *username = NULL, *key_fp = NULL, *keytext = NULL;
855 char *tmp, *command = NULL, **av = NULL;
856 void (*osigchld)(int);
Damien Miller09d3e122012-10-31 08:58:58 +1100857
djm@openbsd.org7c856852018-03-03 03:15:51 +0000858 if (authoptsp != NULL)
859 *authoptsp = NULL;
djm@openbsd.org24232a32015-05-21 06:38:35 +0000860 if (options.authorized_keys_command == NULL)
Damien Miller09d3e122012-10-31 08:58:58 +1100861 return 0;
Damien Millerd0d10992012-11-04 22:23:14 +1100862 if (options.authorized_keys_command_user == NULL) {
863 error("No user for AuthorizedKeysCommand specified, skipping");
864 return 0;
Damien Miller09d3e122012-10-31 08:58:58 +1100865 }
866
djm@openbsd.org24232a32015-05-21 06:38:35 +0000867 /*
868 * NB. all returns later this function should go via "out" to
869 * ensure the original SIGCHLD handler is restored properly.
870 */
871 osigchld = signal(SIGCHLD, SIG_DFL);
872
873 /* Prepare and verify the user for the command */
Damien Millerd0d10992012-11-04 22:23:14 +1100874 username = percent_expand(options.authorized_keys_command_user,
875 "u", user_pw->pw_name, (char *)NULL);
djm@openbsd.org7c856852018-03-03 03:15:51 +0000876 runas_pw = getpwnam(username);
877 if (runas_pw == NULL) {
Damien Miller4018dc02013-02-15 10:28:55 +1100878 error("AuthorizedKeysCommandUser \"%s\" not found: %s",
879 username, strerror(errno));
Damien Miller09d3e122012-10-31 08:58:58 +1100880 goto out;
881 }
882
djm@openbsd.org24232a32015-05-21 06:38:35 +0000883 /* Prepare AuthorizedKeysCommand */
884 if ((key_fp = sshkey_fingerprint(key, options.fingerprint_hash,
885 SSH_FP_DEFAULT)) == NULL) {
886 error("%s: sshkey_fingerprint failed", __func__);
887 goto out;
888 }
889 if ((r = sshkey_to_base64(key, &keytext)) != 0) {
890 error("%s: sshkey_to_base64 failed: %s", __func__, ssh_err(r));
Damien Miller09d3e122012-10-31 08:58:58 +1100891 goto out;
892 }
893
djm@openbsd.org24232a32015-05-21 06:38:35 +0000894 /* Turn the command into an argument vector */
djm@openbsd.orgde4ae072017-08-18 05:36:45 +0000895 if (argv_split(options.authorized_keys_command, &ac, &av) != 0) {
djm@openbsd.org24232a32015-05-21 06:38:35 +0000896 error("AuthorizedKeysCommand \"%s\" contains invalid quotes",
897 command);
898 goto out;
899 }
900 if (ac == 0) {
901 error("AuthorizedKeysCommand \"%s\" yielded no arguments",
902 command);
903 goto out;
904 }
905 for (i = 1; i < ac; i++) {
906 tmp = percent_expand(av[i],
907 "u", user_pw->pw_name,
908 "h", user_pw->pw_dir,
909 "t", sshkey_ssh_name(key),
910 "f", key_fp,
911 "k", keytext,
912 (char *)NULL);
913 if (tmp == NULL)
914 fatal("%s: percent_expand failed", __func__);
915 free(av[i]);
916 av[i] = tmp;
917 }
918 /* Prepare a printable command for logs, etc. */
djm@openbsd.orgde4ae072017-08-18 05:36:45 +0000919 command = argv_assemble(ac, av);
Damien Miller09d3e122012-10-31 08:58:58 +1100920
921 /*
djm@openbsd.org24232a32015-05-21 06:38:35 +0000922 * If AuthorizedKeysCommand was run without arguments
923 * then fall back to the old behaviour of passing the
924 * target username as a single argument.
Damien Miller09d3e122012-10-31 08:58:58 +1100925 */
djm@openbsd.org24232a32015-05-21 06:38:35 +0000926 if (ac == 1) {
927 av = xreallocarray(av, ac + 2, sizeof(*av));
928 av[1] = xstrdup(user_pw->pw_name);
929 av[2] = NULL;
930 /* Fix up command too, since it is used in log messages */
931 free(command);
932 xasprintf(&command, "%s %s", av[0], av[1]);
Damien Miller09d3e122012-10-31 08:58:58 +1100933 }
934
djm@openbsd.org7c856852018-03-03 03:15:51 +0000935 if ((pid = subprocess("AuthorizedKeysCommand", runas_pw, command,
djm@openbsd.orgde4ae072017-08-18 05:36:45 +0000936 ac, av, &f,
937 SSH_SUBPROCESS_STDOUT_CAPTURE|SSH_SUBPROCESS_STDERR_DISCARD)) == 0)
djm@openbsd.org24232a32015-05-21 06:38:35 +0000938 goto out;
939
940 uid_swapped = 1;
djm@openbsd.org7c856852018-03-03 03:15:51 +0000941 temporarily_use_uid(runas_pw);
Damien Miller09d3e122012-10-31 08:58:58 +1100942
djm@openbsd.org7c856852018-03-03 03:15:51 +0000943 ok = check_authkeys_file(ssh, user_pw, f,
944 options.authorized_keys_command, key, authoptsp);
Damien Miller09d3e122012-10-31 08:58:58 +1100945
djm@openbsd.orgddd3d342016-12-30 22:08:02 +0000946 fclose(f);
947 f = NULL;
948
djm@openbsd.orgb074c3c2017-08-18 05:48:04 +0000949 if (exited_cleanly(pid, "AuthorizedKeysCommand", command, 0) != 0)
Damien Miller09d3e122012-10-31 08:58:58 +1100950 goto out;
djm@openbsd.org24232a32015-05-21 06:38:35 +0000951
952 /* Read completed successfully */
Damien Miller09d3e122012-10-31 08:58:58 +1100953 found_key = ok;
954 out:
djm@openbsd.org24232a32015-05-21 06:38:35 +0000955 if (f != NULL)
956 fclose(f);
957 signal(SIGCHLD, osigchld);
958 for (i = 0; i < ac; i++)
959 free(av[i]);
960 free(av);
961 if (uid_swapped)
962 restore_uid();
963 free(command);
964 free(username);
965 free(key_fp);
966 free(keytext);
Damien Miller09d3e122012-10-31 08:58:58 +1100967 return found_key;
968}
969
970/*
971 * Check whether key authenticates and authorises the user.
972 */
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000973int
djm@openbsd.org7c856852018-03-03 03:15:51 +0000974user_key_allowed(struct ssh *ssh, struct passwd *pw, struct sshkey *key,
975 int auth_attempt, struct sshauthopt **authoptsp)
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000976{
Damien Millerd8478b62011-05-29 21:39:36 +1000977 u_int success, i;
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000978 char *file;
djm@openbsd.org7c856852018-03-03 03:15:51 +0000979 struct sshauthopt *opts = NULL;
980 if (authoptsp != NULL)
981 *authoptsp = NULL;
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000982
Damien Miller1aed65e2010-03-04 21:53:35 +1100983 if (auth_key_is_revoked(key))
984 return 0;
markus@openbsd.org00ed75c2017-05-30 14:10:53 +0000985 if (sshkey_is_cert(key) &&
986 auth_key_is_revoked(key->cert->signature_key))
Damien Miller1aed65e2010-03-04 21:53:35 +1100987 return 0;
988
djm@openbsd.org7c856852018-03-03 03:15:51 +0000989 if ((success = user_cert_trusted_ca(ssh, pw, key, &opts)) != 0)
990 goto out;
991 sshauthopt_free(opts);
992 opts = NULL;
Damien Miller1aed65e2010-03-04 21:53:35 +1100993
djm@openbsd.org7c856852018-03-03 03:15:51 +0000994 if ((success = user_key_command_allowed2(ssh, pw, key, &opts)) != 0)
995 goto out;
996 sshauthopt_free(opts);
997 opts = NULL;
Damien Miller09d3e122012-10-31 08:58:58 +1100998
Damien Millerd8478b62011-05-29 21:39:36 +1000999 for (i = 0; !success && i < options.num_authkeys_files; i++) {
Damien Miller09d3e122012-10-31 08:58:58 +11001000 if (strcasecmp(options.authorized_keys_files[i], "none") == 0)
1001 continue;
Damien Millerd8478b62011-05-29 21:39:36 +10001002 file = expand_authorized_keys(
1003 options.authorized_keys_files[i], pw);
djm@openbsd.org7c856852018-03-03 03:15:51 +00001004 success = user_key_allowed2(ssh, pw, key, file, &opts);
Darren Tuckera627d422013-06-02 07:31:17 +10001005 free(file);
Damien Millerd8478b62011-05-29 21:39:36 +10001006 }
Ben Lindstrom855bf3a2002-06-06 20:27:55 +00001007
djm@openbsd.org7c856852018-03-03 03:15:51 +00001008 out:
1009 if (success && authoptsp != NULL) {
1010 *authoptsp = opts;
1011 opts = NULL;
1012 }
1013 sshauthopt_free(opts);
Ben Lindstrom855bf3a2002-06-06 20:27:55 +00001014 return success;
1015}
1016
Ben Lindstrom855bf3a2002-06-06 20:27:55 +00001017Authmethod method_pubkey = {
1018 "publickey",
1019 userauth_pubkey,
1020 &options.pubkey_authentication
1021};