blob: 3ccc3a2130a885817534c4f86f2a292152808820 [file] [log] [blame]
markus@openbsd.org7f906352018-06-06 18:29:18 +00001/* $OpenBSD: auth2-pubkey.c,v 1.79 2018/06/06 18:29:18 markus 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{
markus@openbsd.org7f906352018-06-06 18:29:18 +0000322 char loc[256], *line = NULL, *cp, *ep;
323 size_t linesize = 0;
Damien Miller30da3442010-05-10 11:58:03 +1000324 u_long linenum = 0;
djm@openbsd.org7c856852018-03-03 03:15:51 +0000325 u_int found_principal = 0;
326
327 if (authoptsp != NULL)
328 *authoptsp = NULL;
Damien Miller30da3442010-05-10 11:58:03 +1000329
markus@openbsd.org7f906352018-06-06 18:29:18 +0000330 while (getline(&line, &linesize, f) != -1) {
331 linenum++;
djm@openbsd.org52763dd2017-01-30 01:03:00 +0000332 /* Always consume entire input */
333 if (found_principal)
334 continue;
djm@openbsd.org7c856852018-03-03 03:15:51 +0000335
Damien Miller6018a362010-07-02 13:35:19 +1000336 /* Skip leading whitespace. */
Damien Miller30da3442010-05-10 11:58:03 +1000337 for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
338 ;
Damien Miller6018a362010-07-02 13:35:19 +1000339 /* Skip blank and comment lines. */
340 if ((ep = strchr(cp, '#')) != NULL)
341 *ep = '\0';
342 if (!*cp || *cp == '\n')
Damien Miller30da3442010-05-10 11:58:03 +1000343 continue;
djm@openbsd.org7c856852018-03-03 03:15:51 +0000344
345 snprintf(loc, sizeof(loc), "%.200s:%lu", file, linenum);
346 if (check_principals_line(ssh, cp, cert, loc, authoptsp) == 0)
347 found_principal = 1;
Damien Miller30da3442010-05-10 11:58:03 +1000348 }
markus@openbsd.org7f906352018-06-06 18:29:18 +0000349 free(line);
djm@openbsd.org52763dd2017-01-30 01:03:00 +0000350 return found_principal;
Damien Miller09d3e122012-10-31 08:58:58 +1100351}
Damien Miller30da3442010-05-10 11:58:03 +1000352
djm@openbsd.org7c856852018-03-03 03:15:51 +0000353/* XXX remove pw args here and elsewhere once ssh->authctxt is guaranteed */
354
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000355static int
djm@openbsd.org7c856852018-03-03 03:15:51 +0000356match_principals_file(struct ssh *ssh, struct passwd *pw, char *file,
357 struct sshkey_cert *cert, struct sshauthopt **authoptsp)
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000358{
359 FILE *f;
360 int success;
361
djm@openbsd.org7c856852018-03-03 03:15:51 +0000362 if (authoptsp != NULL)
363 *authoptsp = NULL;
364
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000365 temporarily_use_uid(pw);
366 debug("trying authorized principals file %s", file);
367 if ((f = auth_openprincipals(file, pw, options.strict_modes)) == NULL) {
368 restore_uid();
369 return 0;
370 }
djm@openbsd.org7c856852018-03-03 03:15:51 +0000371 success = process_principals(ssh, f, file, cert, authoptsp);
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000372 fclose(f);
373 restore_uid();
374 return success;
375}
376
377/*
378 * Checks whether principal is allowed in output of command.
379 * returns 1 if the principal is allowed or 0 otherwise.
380 */
381static int
djm@openbsd.org7c856852018-03-03 03:15:51 +0000382match_principals_command(struct ssh *ssh, struct passwd *user_pw,
383 const struct sshkey *key, struct sshauthopt **authoptsp)
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000384{
djm@openbsd.org7c856852018-03-03 03:15:51 +0000385 struct passwd *runas_pw = NULL;
djm@openbsd.orge7907c12016-09-14 05:42:25 +0000386 const struct sshkey_cert *cert = key->cert;
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000387 FILE *f = NULL;
djm@openbsd.orge7907c12016-09-14 05:42:25 +0000388 int r, ok, found_principal = 0;
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000389 int i, ac = 0, uid_swapped = 0;
390 pid_t pid;
391 char *tmp, *username = NULL, *command = NULL, **av = NULL;
djm@openbsd.orge7907c12016-09-14 05:42:25 +0000392 char *ca_fp = NULL, *key_fp = NULL, *catext = NULL, *keytext = NULL;
djm@openbsd.org9c935dd2018-06-01 03:33:53 +0000393 char serial_s[16], uidstr[32];
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000394 void (*osigchld)(int);
395
djm@openbsd.org7c856852018-03-03 03:15:51 +0000396 if (authoptsp != NULL)
397 *authoptsp = NULL;
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000398 if (options.authorized_principals_command == NULL)
399 return 0;
400 if (options.authorized_principals_command_user == NULL) {
401 error("No user for AuthorizedPrincipalsCommand specified, "
402 "skipping");
403 return 0;
404 }
405
406 /*
407 * NB. all returns later this function should go via "out" to
408 * ensure the original SIGCHLD handler is restored properly.
409 */
410 osigchld = signal(SIGCHLD, SIG_DFL);
411
412 /* Prepare and verify the user for the command */
413 username = percent_expand(options.authorized_principals_command_user,
414 "u", user_pw->pw_name, (char *)NULL);
djm@openbsd.org7c856852018-03-03 03:15:51 +0000415 runas_pw = getpwnam(username);
416 if (runas_pw == NULL) {
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000417 error("AuthorizedPrincipalsCommandUser \"%s\" not found: %s",
418 username, strerror(errno));
419 goto out;
420 }
421
422 /* Turn the command into an argument vector */
djm@openbsd.orgde4ae072017-08-18 05:36:45 +0000423 if (argv_split(options.authorized_principals_command, &ac, &av) != 0) {
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000424 error("AuthorizedPrincipalsCommand \"%s\" contains "
425 "invalid quotes", command);
426 goto out;
427 }
428 if (ac == 0) {
429 error("AuthorizedPrincipalsCommand \"%s\" yielded no arguments",
430 command);
431 goto out;
432 }
djm@openbsd.orge7907c12016-09-14 05:42:25 +0000433 if ((ca_fp = sshkey_fingerprint(cert->signature_key,
434 options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) {
435 error("%s: sshkey_fingerprint failed", __func__);
436 goto out;
437 }
djm@openbsd.org00df97f2016-09-14 20:11:26 +0000438 if ((key_fp = sshkey_fingerprint(key,
djm@openbsd.orge7907c12016-09-14 05:42:25 +0000439 options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) {
440 error("%s: sshkey_fingerprint failed", __func__);
441 goto out;
442 }
443 if ((r = sshkey_to_base64(cert->signature_key, &catext)) != 0) {
444 error("%s: sshkey_to_base64 failed: %s", __func__, ssh_err(r));
445 goto out;
446 }
447 if ((r = sshkey_to_base64(key, &keytext)) != 0) {
448 error("%s: sshkey_to_base64 failed: %s", __func__, ssh_err(r));
449 goto out;
450 }
djm@openbsd.orgf83a0cf2016-09-21 17:44:20 +0000451 snprintf(serial_s, sizeof(serial_s), "%llu",
452 (unsigned long long)cert->serial);
djm@openbsd.org9c935dd2018-06-01 03:33:53 +0000453 snprintf(uidstr, sizeof(uidstr), "%llu",
454 (unsigned long long)user_pw->pw_uid);
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000455 for (i = 1; i < ac; i++) {
456 tmp = percent_expand(av[i],
djm@openbsd.org9c935dd2018-06-01 03:33:53 +0000457 "U", uidstr,
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000458 "u", user_pw->pw_name,
459 "h", user_pw->pw_dir,
djm@openbsd.orge7907c12016-09-14 05:42:25 +0000460 "t", sshkey_ssh_name(key),
461 "T", sshkey_ssh_name(cert->signature_key),
462 "f", key_fp,
463 "F", ca_fp,
464 "k", keytext,
465 "K", catext,
djm@openbsd.orgbfa9d962016-09-21 01:34:45 +0000466 "i", cert->key_id,
467 "s", serial_s,
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000468 (char *)NULL);
469 if (tmp == NULL)
470 fatal("%s: percent_expand failed", __func__);
471 free(av[i]);
472 av[i] = tmp;
473 }
474 /* Prepare a printable command for logs, etc. */
djm@openbsd.orgde4ae072017-08-18 05:36:45 +0000475 command = argv_assemble(ac, av);
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000476
djm@openbsd.org7c856852018-03-03 03:15:51 +0000477 if ((pid = subprocess("AuthorizedPrincipalsCommand", runas_pw, command,
djm@openbsd.orgde4ae072017-08-18 05:36:45 +0000478 ac, av, &f,
479 SSH_SUBPROCESS_STDOUT_CAPTURE|SSH_SUBPROCESS_STDERR_DISCARD)) == 0)
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000480 goto out;
481
482 uid_swapped = 1;
djm@openbsd.org7c856852018-03-03 03:15:51 +0000483 temporarily_use_uid(runas_pw);
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000484
djm@openbsd.org7c856852018-03-03 03:15:51 +0000485 ok = process_principals(ssh, f, "(command)", cert, authoptsp);
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000486
djm@openbsd.orgddd3d342016-12-30 22:08:02 +0000487 fclose(f);
488 f = NULL;
489
djm@openbsd.orgb074c3c2017-08-18 05:48:04 +0000490 if (exited_cleanly(pid, "AuthorizedPrincipalsCommand", command, 0) != 0)
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000491 goto out;
492
493 /* Read completed successfully */
494 found_principal = ok;
495 out:
496 if (f != NULL)
497 fclose(f);
498 signal(SIGCHLD, osigchld);
499 for (i = 0; i < ac; i++)
500 free(av[i]);
501 free(av);
502 if (uid_swapped)
503 restore_uid();
504 free(command);
505 free(username);
djm@openbsd.orge7907c12016-09-14 05:42:25 +0000506 free(ca_fp);
507 free(key_fp);
508 free(catext);
509 free(keytext);
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000510 return found_principal;
511}
djm@openbsd.org7c856852018-03-03 03:15:51 +0000512
513static void
514skip_space(char **cpp)
515{
516 char *cp;
517
518 for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++)
519 ;
520 *cpp = cp;
521}
522
523/*
524 * Advanced *cpp past the end of key options, defined as the first unquoted
525 * whitespace character. Returns 0 on success or -1 on failure (e.g.
526 * unterminated quotes).
527 */
528static int
529advance_past_options(char **cpp)
530{
531 char *cp = *cpp;
532 int quoted = 0;
533
534 for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
535 if (*cp == '\\' && cp[1] == '"')
536 cp++; /* Skip both */
537 else if (*cp == '"')
538 quoted = !quoted;
539 }
540 *cpp = cp;
541 /* return failure for unterminated quotes */
542 return (*cp == '\0' && quoted) ? -1 : 0;
543}
544
545/*
546 * Check a single line of an authorized_keys-format file. Returns 0 if key
547 * matches, -1 otherwise. Will return key/cert options via *authoptsp
548 * on success. "loc" is used as file/line location in log messages.
549 */
550static int
551check_authkey_line(struct ssh *ssh, struct passwd *pw, struct sshkey *key,
552 char *cp, const char *loc, struct sshauthopt **authoptsp)
553{
554 int want_keytype = sshkey_is_cert(key) ? KEY_UNSPEC : key->type;
555 struct sshkey *found = NULL;
556 struct sshauthopt *keyopts = NULL, *certopts = NULL, *finalopts = NULL;
557 char *key_options = NULL, *fp = NULL;
558 const char *reason = NULL;
559 int ret = -1;
560
561 if (authoptsp != NULL)
562 *authoptsp = NULL;
563
564 if ((found = sshkey_new(want_keytype)) == NULL) {
565 debug3("%s: keytype %d failed", __func__, want_keytype);
566 goto out;
567 }
568
569 /* XXX djm: peek at key type in line and skip if unwanted */
570
571 if (sshkey_read(found, &cp) != 0) {
572 /* no key? check for options */
573 debug2("%s: check options: '%s'", loc, cp);
574 key_options = cp;
575 if (advance_past_options(&cp) != 0) {
576 reason = "invalid key option string";
577 goto fail_reason;
578 }
579 skip_space(&cp);
580 if (sshkey_read(found, &cp) != 0) {
581 /* still no key? advance to next line*/
582 debug2("%s: advance: '%s'", loc, cp);
583 goto out;
584 }
585 }
586 /* Parse key options now; we need to know if this is a CA key */
587 if ((keyopts = sshauthopt_parse(key_options, &reason)) == NULL) {
588 debug("%s: bad key options: %s", loc, reason);
589 auth_debug_add("%s: bad key options: %s", loc, reason);
590 goto out;
591 }
592 /* Ignore keys that don't match or incorrectly marked as CAs */
593 if (sshkey_is_cert(key)) {
594 /* Certificate; check signature key against CA */
595 if (!sshkey_equal(found, key->cert->signature_key) ||
596 !keyopts->cert_authority)
597 goto out;
598 } else {
599 /* Plain key: check it against key found in file */
600 if (!sshkey_equal(found, key) || keyopts->cert_authority)
601 goto out;
602 }
603
604 /* We have a candidate key, perform authorisation checks */
605 if ((fp = sshkey_fingerprint(found,
606 options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
607 fatal("%s: fingerprint failed", __func__);
608
609 debug("%s: matching %s found: %s %s", loc,
610 sshkey_is_cert(key) ? "CA" : "key", sshkey_type(found), fp);
611
612 if (auth_authorise_keyopts(ssh, pw, keyopts,
613 sshkey_is_cert(key), loc) != 0) {
614 reason = "Refused by key options";
615 goto fail_reason;
616 }
617 /* That's all we need for plain keys. */
618 if (!sshkey_is_cert(key)) {
619 verbose("Accepted key %s %s found at %s",
620 sshkey_type(found), fp, loc);
621 finalopts = keyopts;
622 keyopts = NULL;
623 goto success;
624 }
625
626 /*
627 * Additional authorisation for certificates.
628 */
629
630 /* Parse and check options present in certificate */
631 if ((certopts = sshauthopt_from_cert(key)) == NULL) {
632 reason = "Invalid certificate options";
633 goto fail_reason;
634 }
635 if (auth_authorise_keyopts(ssh, pw, certopts, 0, loc) != 0) {
636 reason = "Refused by certificate options";
637 goto fail_reason;
638 }
639 if ((finalopts = sshauthopt_merge(keyopts, certopts, &reason)) == NULL)
640 goto fail_reason;
641
642 /*
643 * If the user has specified a list of principals as
644 * a key option, then prefer that list to matching
645 * their username in the certificate principals list.
646 */
647 if (keyopts->cert_principals != NULL &&
648 !match_principals_option(keyopts->cert_principals, key->cert)) {
649 reason = "Certificate does not contain an authorized principal";
650 goto fail_reason;
651 }
652 if (sshkey_cert_check_authority(key, 0, 0,
653 keyopts->cert_principals == NULL ? pw->pw_name : NULL, &reason) != 0)
654 goto fail_reason;
655
656 verbose("Accepted certificate ID \"%s\" (serial %llu) "
657 "signed by CA %s %s found at %s",
658 key->cert->key_id,
659 (unsigned long long)key->cert->serial,
660 sshkey_type(found), fp, loc);
661
662 success:
663 if (finalopts == NULL)
664 fatal("%s: internal error: missing options", __func__);
665 if (authoptsp != NULL) {
666 *authoptsp = finalopts;
667 finalopts = NULL;
668 }
669 /* success */
670 ret = 0;
671 goto out;
672
673 fail_reason:
674 error("%s", reason);
675 auth_debug_add("%s", reason);
676 out:
677 free(fp);
678 sshauthopt_free(keyopts);
679 sshauthopt_free(certopts);
680 sshauthopt_free(finalopts);
681 sshkey_free(found);
682 return ret;
683}
684
Damien Miller09d3e122012-10-31 08:58:58 +1100685/*
686 * Checks whether key is allowed in authorized_keys-format file,
687 * returns 1 if the key is allowed or 0 otherwise.
688 */
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000689static int
djm@openbsd.org7c856852018-03-03 03:15:51 +0000690check_authkeys_file(struct ssh *ssh, struct passwd *pw, FILE *f,
691 char *file, struct sshkey *key, struct sshauthopt **authoptsp)
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000692{
markus@openbsd.org7f906352018-06-06 18:29:18 +0000693 char *cp, *line = NULL, loc[256];
694 size_t linesize = 0;
Darren Tucker33c787f2008-07-02 22:37:30 +1000695 int found_key = 0;
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000696 u_long linenum = 0;
djm@openbsd.org7c856852018-03-03 03:15:51 +0000697
698 if (authoptsp != NULL)
699 *authoptsp = NULL;
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000700
markus@openbsd.org7f906352018-06-06 18:29:18 +0000701 while (getline(&line, &linesize, f) != -1) {
702 linenum++;
djm@openbsd.orgabd59662017-09-07 23:48:09 +0000703 /* Always consume entire file */
djm@openbsd.org52763dd2017-01-30 01:03:00 +0000704 if (found_key)
705 continue;
Damien Miller0a80ca12010-02-27 07:55:05 +1100706
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000707 /* Skip leading whitespace, empty and comment lines. */
djm@openbsd.org7c856852018-03-03 03:15:51 +0000708 cp = line;
709 skip_space(&cp);
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000710 if (!*cp || *cp == '\n' || *cp == '#')
711 continue;
djm@openbsd.org7c856852018-03-03 03:15:51 +0000712 snprintf(loc, sizeof(loc), "%.200s:%lu", file, linenum);
713 if (check_authkey_line(ssh, pw, key, cp, loc, authoptsp) == 0)
Damien Miller0a80ca12010-02-27 07:55:05 +1100714 found_key = 1;
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000715 }
markus@openbsd.org7f906352018-06-06 18:29:18 +0000716 free(line);
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000717 return found_key;
718}
719
Damien Miller1aed65e2010-03-04 21:53:35 +1100720/* Authenticate a certificate key against TrustedUserCAKeys */
721static int
djm@openbsd.org7c856852018-03-03 03:15:51 +0000722user_cert_trusted_ca(struct ssh *ssh, struct passwd *pw, struct sshkey *key,
723 struct sshauthopt **authoptsp)
Damien Miller1aed65e2010-03-04 21:53:35 +1100724{
Damien Miller30da3442010-05-10 11:58:03 +1000725 char *ca_fp, *principals_file = NULL;
Damien Miller1aed65e2010-03-04 21:53:35 +1100726 const char *reason;
djm@openbsd.org7c856852018-03-03 03:15:51 +0000727 struct sshauthopt *principals_opts = NULL, *cert_opts = NULL;
728 struct sshauthopt *final_opts = NULL;
markus@openbsd.org00ed75c2017-05-30 14:10:53 +0000729 int r, ret = 0, found_principal = 0, use_authorized_principals;
Damien Miller1aed65e2010-03-04 21:53:35 +1100730
djm@openbsd.org7c856852018-03-03 03:15:51 +0000731 if (authoptsp != NULL)
732 *authoptsp = NULL;
733
markus@openbsd.org00ed75c2017-05-30 14:10:53 +0000734 if (!sshkey_is_cert(key) || options.trusted_user_ca_keys == NULL)
Damien Miller1aed65e2010-03-04 21:53:35 +1100735 return 0;
736
djm@openbsd.org9ce86c92015-01-28 22:36:00 +0000737 if ((ca_fp = sshkey_fingerprint(key->cert->signature_key,
738 options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
739 return 0;
Damien Miller1aed65e2010-03-04 21:53:35 +1100740
markus@openbsd.org00ed75c2017-05-30 14:10:53 +0000741 if ((r = sshkey_in_file(key->cert->signature_key,
742 options.trusted_user_ca_keys, 1, 0)) != 0) {
743 debug2("%s: CA %s %s is not listed in %s: %s", __func__,
744 sshkey_type(key->cert->signature_key), ca_fp,
745 options.trusted_user_ca_keys, ssh_err(r));
Damien Miller1aed65e2010-03-04 21:53:35 +1100746 goto out;
747 }
Damien Miller30da3442010-05-10 11:58:03 +1000748 /*
749 * If AuthorizedPrincipals is in use, then compare the certificate
750 * principals against the names in that file rather than matching
751 * against the username.
752 */
753 if ((principals_file = authorized_principals_file(pw)) != NULL) {
djm@openbsd.org7c856852018-03-03 03:15:51 +0000754 if (match_principals_file(ssh, pw, principals_file,
755 key->cert, &principals_opts))
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000756 found_principal = 1;
757 }
758 /* Try querying command if specified */
djm@openbsd.org7c856852018-03-03 03:15:51 +0000759 if (!found_principal && match_principals_command(ssh, pw, key,
760 &principals_opts))
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000761 found_principal = 1;
jsing@openbsd.org596dbca2015-06-15 18:44:22 +0000762 /* If principals file or command is specified, then require a match */
763 use_authorized_principals = principals_file != NULL ||
764 options.authorized_principals_command != NULL;
765 if (!found_principal && use_authorized_principals) {
djm@openbsd.orgbcc50d82015-05-21 06:43:30 +0000766 reason = "Certificate does not contain an authorized principal";
djm@openbsd.org7c856852018-03-03 03:15:51 +0000767 goto fail_reason;
Damien Miller1aed65e2010-03-04 21:53:35 +1100768 }
djm@openbsd.org7c856852018-03-03 03:15:51 +0000769 if (use_authorized_principals && principals_opts == NULL)
770 fatal("%s: internal error: missing principals_opts", __func__);
markus@openbsd.org00ed75c2017-05-30 14:10:53 +0000771 if (sshkey_cert_check_authority(key, 0, 1,
jsing@openbsd.org596dbca2015-06-15 18:44:22 +0000772 use_authorized_principals ? NULL : pw->pw_name, &reason) != 0)
Damien Miller30da3442010-05-10 11:58:03 +1000773 goto fail_reason;
Damien Miller1aed65e2010-03-04 21:53:35 +1100774
djm@openbsd.org7c856852018-03-03 03:15:51 +0000775 /* Check authority from options in key and from principals file/cmd */
776 if ((cert_opts = sshauthopt_from_cert(key)) == NULL) {
777 reason = "Invalid certificate options";
778 goto fail_reason;
779 }
780 if (auth_authorise_keyopts(ssh, pw, cert_opts, 0, "cert") != 0) {
781 reason = "Refused by certificate options";
782 goto fail_reason;
783 }
784 if (principals_opts == NULL) {
785 final_opts = cert_opts;
786 cert_opts = NULL;
787 } else {
788 if (auth_authorise_keyopts(ssh, pw, principals_opts, 0,
789 "principals") != 0) {
790 reason = "Refused by certificate principals options";
791 goto fail_reason;
792 }
793 if ((final_opts = sshauthopt_merge(principals_opts,
794 cert_opts, &reason)) == NULL) {
795 fail_reason:
796 error("%s", reason);
797 auth_debug_add("%s", reason);
798 goto out;
799 }
800 }
801
802 /* Success */
djm@openbsd.org63d18812015-10-27 01:44:45 +0000803 verbose("Accepted certificate ID \"%s\" (serial %llu) signed by "
804 "%s CA %s via %s", key->cert->key_id,
805 (unsigned long long)key->cert->serial,
markus@openbsd.org00ed75c2017-05-30 14:10:53 +0000806 sshkey_type(key->cert->signature_key), ca_fp,
Damien Millere513a912010-03-22 05:51:21 +1100807 options.trusted_user_ca_keys);
djm@openbsd.org7c856852018-03-03 03:15:51 +0000808 if (authoptsp != NULL) {
809 *authoptsp = final_opts;
810 final_opts = NULL;
811 }
Damien Miller1aed65e2010-03-04 21:53:35 +1100812 ret = 1;
Damien Miller1aed65e2010-03-04 21:53:35 +1100813 out:
djm@openbsd.org7c856852018-03-03 03:15:51 +0000814 sshauthopt_free(principals_opts);
815 sshauthopt_free(cert_opts);
816 sshauthopt_free(final_opts);
Darren Tuckera627d422013-06-02 07:31:17 +1000817 free(principals_file);
818 free(ca_fp);
Damien Miller1aed65e2010-03-04 21:53:35 +1100819 return ret;
820}
821
Damien Miller09d3e122012-10-31 08:58:58 +1100822/*
823 * Checks whether key is allowed in file.
824 * returns 1 if the key is allowed or 0 otherwise.
825 */
826static int
djm@openbsd.org7c856852018-03-03 03:15:51 +0000827user_key_allowed2(struct ssh *ssh, struct passwd *pw, struct sshkey *key,
828 char *file, struct sshauthopt **authoptsp)
Damien Miller09d3e122012-10-31 08:58:58 +1100829{
830 FILE *f;
831 int found_key = 0;
832
djm@openbsd.org7c856852018-03-03 03:15:51 +0000833 if (authoptsp != NULL)
834 *authoptsp = NULL;
835
Damien Miller09d3e122012-10-31 08:58:58 +1100836 /* Temporarily use the user's uid. */
837 temporarily_use_uid(pw);
838
839 debug("trying public key file %s", file);
840 if ((f = auth_openkeyfile(file, pw, options.strict_modes)) != NULL) {
djm@openbsd.org7c856852018-03-03 03:15:51 +0000841 found_key = check_authkeys_file(ssh, pw, f, file,
842 key, authoptsp);
Damien Miller09d3e122012-10-31 08:58:58 +1100843 fclose(f);
844 }
845
846 restore_uid();
847 return found_key;
848}
849
850/*
851 * Checks whether key is allowed in output of command.
852 * returns 1 if the key is allowed or 0 otherwise.
853 */
854static int
djm@openbsd.org7c856852018-03-03 03:15:51 +0000855user_key_command_allowed2(struct ssh *ssh, struct passwd *user_pw,
856 struct sshkey *key, struct sshauthopt **authoptsp)
Damien Miller09d3e122012-10-31 08:58:58 +1100857{
djm@openbsd.org7c856852018-03-03 03:15:51 +0000858 struct passwd *runas_pw = NULL;
djm@openbsd.org24232a32015-05-21 06:38:35 +0000859 FILE *f = NULL;
860 int r, ok, found_key = 0;
djm@openbsd.org24232a32015-05-21 06:38:35 +0000861 int i, uid_swapped = 0, ac = 0;
Damien Miller09d3e122012-10-31 08:58:58 +1100862 pid_t pid;
djm@openbsd.org24232a32015-05-21 06:38:35 +0000863 char *username = NULL, *key_fp = NULL, *keytext = NULL;
djm@openbsd.org9c935dd2018-06-01 03:33:53 +0000864 char uidstr[32], *tmp, *command = NULL, **av = NULL;
djm@openbsd.org24232a32015-05-21 06:38:35 +0000865 void (*osigchld)(int);
Damien Miller09d3e122012-10-31 08:58:58 +1100866
djm@openbsd.org7c856852018-03-03 03:15:51 +0000867 if (authoptsp != NULL)
868 *authoptsp = NULL;
djm@openbsd.org24232a32015-05-21 06:38:35 +0000869 if (options.authorized_keys_command == NULL)
Damien Miller09d3e122012-10-31 08:58:58 +1100870 return 0;
Damien Millerd0d10992012-11-04 22:23:14 +1100871 if (options.authorized_keys_command_user == NULL) {
872 error("No user for AuthorizedKeysCommand specified, skipping");
873 return 0;
Damien Miller09d3e122012-10-31 08:58:58 +1100874 }
875
djm@openbsd.org24232a32015-05-21 06:38:35 +0000876 /*
877 * NB. all returns later this function should go via "out" to
878 * ensure the original SIGCHLD handler is restored properly.
879 */
880 osigchld = signal(SIGCHLD, SIG_DFL);
881
882 /* Prepare and verify the user for the command */
Damien Millerd0d10992012-11-04 22:23:14 +1100883 username = percent_expand(options.authorized_keys_command_user,
884 "u", user_pw->pw_name, (char *)NULL);
djm@openbsd.org7c856852018-03-03 03:15:51 +0000885 runas_pw = getpwnam(username);
886 if (runas_pw == NULL) {
Damien Miller4018dc02013-02-15 10:28:55 +1100887 error("AuthorizedKeysCommandUser \"%s\" not found: %s",
888 username, strerror(errno));
Damien Miller09d3e122012-10-31 08:58:58 +1100889 goto out;
890 }
891
djm@openbsd.org24232a32015-05-21 06:38:35 +0000892 /* Prepare AuthorizedKeysCommand */
893 if ((key_fp = sshkey_fingerprint(key, options.fingerprint_hash,
894 SSH_FP_DEFAULT)) == NULL) {
895 error("%s: sshkey_fingerprint failed", __func__);
896 goto out;
897 }
898 if ((r = sshkey_to_base64(key, &keytext)) != 0) {
899 error("%s: sshkey_to_base64 failed: %s", __func__, ssh_err(r));
Damien Miller09d3e122012-10-31 08:58:58 +1100900 goto out;
901 }
902
djm@openbsd.org24232a32015-05-21 06:38:35 +0000903 /* Turn the command into an argument vector */
djm@openbsd.orgde4ae072017-08-18 05:36:45 +0000904 if (argv_split(options.authorized_keys_command, &ac, &av) != 0) {
djm@openbsd.org24232a32015-05-21 06:38:35 +0000905 error("AuthorizedKeysCommand \"%s\" contains invalid quotes",
906 command);
907 goto out;
908 }
909 if (ac == 0) {
910 error("AuthorizedKeysCommand \"%s\" yielded no arguments",
911 command);
912 goto out;
913 }
djm@openbsd.org9c935dd2018-06-01 03:33:53 +0000914 snprintf(uidstr, sizeof(uidstr), "%llu",
915 (unsigned long long)user_pw->pw_uid);
djm@openbsd.org24232a32015-05-21 06:38:35 +0000916 for (i = 1; i < ac; i++) {
917 tmp = percent_expand(av[i],
djm@openbsd.org9c935dd2018-06-01 03:33:53 +0000918 "U", uidstr,
djm@openbsd.org24232a32015-05-21 06:38:35 +0000919 "u", user_pw->pw_name,
920 "h", user_pw->pw_dir,
921 "t", sshkey_ssh_name(key),
922 "f", key_fp,
923 "k", keytext,
924 (char *)NULL);
925 if (tmp == NULL)
926 fatal("%s: percent_expand failed", __func__);
927 free(av[i]);
928 av[i] = tmp;
929 }
930 /* Prepare a printable command for logs, etc. */
djm@openbsd.orgde4ae072017-08-18 05:36:45 +0000931 command = argv_assemble(ac, av);
Damien Miller09d3e122012-10-31 08:58:58 +1100932
933 /*
djm@openbsd.org24232a32015-05-21 06:38:35 +0000934 * If AuthorizedKeysCommand was run without arguments
935 * then fall back to the old behaviour of passing the
936 * target username as a single argument.
Damien Miller09d3e122012-10-31 08:58:58 +1100937 */
djm@openbsd.org24232a32015-05-21 06:38:35 +0000938 if (ac == 1) {
939 av = xreallocarray(av, ac + 2, sizeof(*av));
940 av[1] = xstrdup(user_pw->pw_name);
941 av[2] = NULL;
942 /* Fix up command too, since it is used in log messages */
943 free(command);
944 xasprintf(&command, "%s %s", av[0], av[1]);
Damien Miller09d3e122012-10-31 08:58:58 +1100945 }
946
djm@openbsd.org7c856852018-03-03 03:15:51 +0000947 if ((pid = subprocess("AuthorizedKeysCommand", runas_pw, command,
djm@openbsd.orgde4ae072017-08-18 05:36:45 +0000948 ac, av, &f,
949 SSH_SUBPROCESS_STDOUT_CAPTURE|SSH_SUBPROCESS_STDERR_DISCARD)) == 0)
djm@openbsd.org24232a32015-05-21 06:38:35 +0000950 goto out;
951
952 uid_swapped = 1;
djm@openbsd.org7c856852018-03-03 03:15:51 +0000953 temporarily_use_uid(runas_pw);
Damien Miller09d3e122012-10-31 08:58:58 +1100954
djm@openbsd.org7c856852018-03-03 03:15:51 +0000955 ok = check_authkeys_file(ssh, user_pw, f,
956 options.authorized_keys_command, key, authoptsp);
Damien Miller09d3e122012-10-31 08:58:58 +1100957
djm@openbsd.orgddd3d342016-12-30 22:08:02 +0000958 fclose(f);
959 f = NULL;
960
djm@openbsd.orgb074c3c2017-08-18 05:48:04 +0000961 if (exited_cleanly(pid, "AuthorizedKeysCommand", command, 0) != 0)
Damien Miller09d3e122012-10-31 08:58:58 +1100962 goto out;
djm@openbsd.org24232a32015-05-21 06:38:35 +0000963
964 /* Read completed successfully */
Damien Miller09d3e122012-10-31 08:58:58 +1100965 found_key = ok;
966 out:
djm@openbsd.org24232a32015-05-21 06:38:35 +0000967 if (f != NULL)
968 fclose(f);
969 signal(SIGCHLD, osigchld);
970 for (i = 0; i < ac; i++)
971 free(av[i]);
972 free(av);
973 if (uid_swapped)
974 restore_uid();
975 free(command);
976 free(username);
977 free(key_fp);
978 free(keytext);
Damien Miller09d3e122012-10-31 08:58:58 +1100979 return found_key;
980}
981
982/*
983 * Check whether key authenticates and authorises the user.
984 */
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000985int
djm@openbsd.org7c856852018-03-03 03:15:51 +0000986user_key_allowed(struct ssh *ssh, struct passwd *pw, struct sshkey *key,
987 int auth_attempt, struct sshauthopt **authoptsp)
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000988{
Damien Millerd8478b62011-05-29 21:39:36 +1000989 u_int success, i;
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000990 char *file;
djm@openbsd.org7c856852018-03-03 03:15:51 +0000991 struct sshauthopt *opts = NULL;
992 if (authoptsp != NULL)
993 *authoptsp = NULL;
Ben Lindstrom855bf3a2002-06-06 20:27:55 +0000994
Damien Miller1aed65e2010-03-04 21:53:35 +1100995 if (auth_key_is_revoked(key))
996 return 0;
markus@openbsd.org00ed75c2017-05-30 14:10:53 +0000997 if (sshkey_is_cert(key) &&
998 auth_key_is_revoked(key->cert->signature_key))
Damien Miller1aed65e2010-03-04 21:53:35 +1100999 return 0;
1000
djm@openbsd.org7c856852018-03-03 03:15:51 +00001001 if ((success = user_cert_trusted_ca(ssh, pw, key, &opts)) != 0)
1002 goto out;
1003 sshauthopt_free(opts);
1004 opts = NULL;
Damien Miller1aed65e2010-03-04 21:53:35 +11001005
djm@openbsd.org7c856852018-03-03 03:15:51 +00001006 if ((success = user_key_command_allowed2(ssh, pw, key, &opts)) != 0)
1007 goto out;
1008 sshauthopt_free(opts);
1009 opts = NULL;
Damien Miller09d3e122012-10-31 08:58:58 +11001010
Damien Millerd8478b62011-05-29 21:39:36 +10001011 for (i = 0; !success && i < options.num_authkeys_files; i++) {
Damien Miller09d3e122012-10-31 08:58:58 +11001012 if (strcasecmp(options.authorized_keys_files[i], "none") == 0)
1013 continue;
Damien Millerd8478b62011-05-29 21:39:36 +10001014 file = expand_authorized_keys(
1015 options.authorized_keys_files[i], pw);
djm@openbsd.org7c856852018-03-03 03:15:51 +00001016 success = user_key_allowed2(ssh, pw, key, file, &opts);
Darren Tuckera627d422013-06-02 07:31:17 +10001017 free(file);
Damien Millerd8478b62011-05-29 21:39:36 +10001018 }
Ben Lindstrom855bf3a2002-06-06 20:27:55 +00001019
djm@openbsd.org7c856852018-03-03 03:15:51 +00001020 out:
1021 if (success && authoptsp != NULL) {
1022 *authoptsp = opts;
1023 opts = NULL;
1024 }
1025 sshauthopt_free(opts);
Ben Lindstrom855bf3a2002-06-06 20:27:55 +00001026 return success;
1027}
1028
Ben Lindstrom855bf3a2002-06-06 20:27:55 +00001029Authmethod method_pubkey = {
1030 "publickey",
1031 userauth_pubkey,
1032 &options.pubkey_authentication
1033};