xref: /dflybsd-src/crypto/openssh/ssh-keysign.c (revision 856ea928c995e02ea88a6865c1f1983842decc66)
1*856ea928SPeter Avalos /* $OpenBSD: ssh-keysign.c,v 1.32 2010/08/04 06:08:40 djm Exp $ */
218de8d7fSPeter Avalos /*
318de8d7fSPeter Avalos  * Copyright (c) 2002 Markus Friedl.  All rights reserved.
418de8d7fSPeter Avalos  *
518de8d7fSPeter Avalos  * Redistribution and use in source and binary forms, with or without
618de8d7fSPeter Avalos  * modification, are permitted provided that the following conditions
718de8d7fSPeter Avalos  * are met:
818de8d7fSPeter Avalos  * 1. Redistributions of source code must retain the above copyright
918de8d7fSPeter Avalos  *    notice, this list of conditions and the following disclaimer.
1018de8d7fSPeter Avalos  * 2. Redistributions in binary form must reproduce the above copyright
1118de8d7fSPeter Avalos  *    notice, this list of conditions and the following disclaimer in the
1218de8d7fSPeter Avalos  *    documentation and/or other materials provided with the distribution.
1318de8d7fSPeter Avalos  *
1418de8d7fSPeter Avalos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1518de8d7fSPeter Avalos  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1618de8d7fSPeter Avalos  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1718de8d7fSPeter Avalos  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1818de8d7fSPeter Avalos  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1918de8d7fSPeter Avalos  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2018de8d7fSPeter Avalos  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2118de8d7fSPeter Avalos  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2218de8d7fSPeter Avalos  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2318de8d7fSPeter Avalos  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2418de8d7fSPeter Avalos  */
2518de8d7fSPeter Avalos 
2618de8d7fSPeter Avalos #include "includes.h"
2718de8d7fSPeter Avalos 
2818de8d7fSPeter Avalos #include <fcntl.h>
2918de8d7fSPeter Avalos #ifdef HAVE_PATHS_H
3018de8d7fSPeter Avalos #include <paths.h>
3118de8d7fSPeter Avalos #endif
3218de8d7fSPeter Avalos #include <pwd.h>
3318de8d7fSPeter Avalos #include <stdarg.h>
3418de8d7fSPeter Avalos #include <stdlib.h>
3518de8d7fSPeter Avalos #include <string.h>
3618de8d7fSPeter Avalos #include <unistd.h>
3718de8d7fSPeter Avalos 
3818de8d7fSPeter Avalos #include <openssl/evp.h>
3918de8d7fSPeter Avalos #include <openssl/rand.h>
4018de8d7fSPeter Avalos #include <openssl/rsa.h>
4118de8d7fSPeter Avalos 
4218de8d7fSPeter Avalos #include "xmalloc.h"
4318de8d7fSPeter Avalos #include "log.h"
4418de8d7fSPeter Avalos #include "key.h"
4518de8d7fSPeter Avalos #include "ssh.h"
4618de8d7fSPeter Avalos #include "ssh2.h"
4718de8d7fSPeter Avalos #include "misc.h"
4818de8d7fSPeter Avalos #include "buffer.h"
4918de8d7fSPeter Avalos #include "authfile.h"
5018de8d7fSPeter Avalos #include "msg.h"
5118de8d7fSPeter Avalos #include "canohost.h"
5218de8d7fSPeter Avalos #include "pathnames.h"
5318de8d7fSPeter Avalos #include "readconf.h"
5418de8d7fSPeter Avalos #include "uidswap.h"
5518de8d7fSPeter Avalos 
5618de8d7fSPeter Avalos /* XXX readconf.c needs these */
5718de8d7fSPeter Avalos uid_t original_real_uid;
5818de8d7fSPeter Avalos 
5918de8d7fSPeter Avalos extern char *__progname;
6018de8d7fSPeter Avalos 
6118de8d7fSPeter Avalos static int
6218de8d7fSPeter Avalos valid_request(struct passwd *pw, char *host, Key **ret, u_char *data,
6318de8d7fSPeter Avalos     u_int datalen)
6418de8d7fSPeter Avalos {
6518de8d7fSPeter Avalos 	Buffer b;
6618de8d7fSPeter Avalos 	Key *key = NULL;
6718de8d7fSPeter Avalos 	u_char *pkblob;
6818de8d7fSPeter Avalos 	u_int blen, len;
6918de8d7fSPeter Avalos 	char *pkalg, *p;
7018de8d7fSPeter Avalos 	int pktype, fail;
7118de8d7fSPeter Avalos 
7218de8d7fSPeter Avalos 	fail = 0;
7318de8d7fSPeter Avalos 
7418de8d7fSPeter Avalos 	buffer_init(&b);
7518de8d7fSPeter Avalos 	buffer_append(&b, data, datalen);
7618de8d7fSPeter Avalos 
7718de8d7fSPeter Avalos 	/* session id, currently limited to SHA1 (20 bytes) or SHA256 (32) */
7818de8d7fSPeter Avalos 	p = buffer_get_string(&b, &len);
7918de8d7fSPeter Avalos 	if (len != 20 && len != 32)
8018de8d7fSPeter Avalos 		fail++;
8118de8d7fSPeter Avalos 	xfree(p);
8218de8d7fSPeter Avalos 
8318de8d7fSPeter Avalos 	if (buffer_get_char(&b) != SSH2_MSG_USERAUTH_REQUEST)
8418de8d7fSPeter Avalos 		fail++;
8518de8d7fSPeter Avalos 
8618de8d7fSPeter Avalos 	/* server user */
8718de8d7fSPeter Avalos 	buffer_skip_string(&b);
8818de8d7fSPeter Avalos 
8918de8d7fSPeter Avalos 	/* service */
9018de8d7fSPeter Avalos 	p = buffer_get_string(&b, NULL);
9118de8d7fSPeter Avalos 	if (strcmp("ssh-connection", p) != 0)
9218de8d7fSPeter Avalos 		fail++;
9318de8d7fSPeter Avalos 	xfree(p);
9418de8d7fSPeter Avalos 
9518de8d7fSPeter Avalos 	/* method */
9618de8d7fSPeter Avalos 	p = buffer_get_string(&b, NULL);
9718de8d7fSPeter Avalos 	if (strcmp("hostbased", p) != 0)
9818de8d7fSPeter Avalos 		fail++;
9918de8d7fSPeter Avalos 	xfree(p);
10018de8d7fSPeter Avalos 
10118de8d7fSPeter Avalos 	/* pubkey */
10218de8d7fSPeter Avalos 	pkalg = buffer_get_string(&b, NULL);
10318de8d7fSPeter Avalos 	pkblob = buffer_get_string(&b, &blen);
10418de8d7fSPeter Avalos 
10518de8d7fSPeter Avalos 	pktype = key_type_from_name(pkalg);
10618de8d7fSPeter Avalos 	if (pktype == KEY_UNSPEC)
10718de8d7fSPeter Avalos 		fail++;
10818de8d7fSPeter Avalos 	else if ((key = key_from_blob(pkblob, blen)) == NULL)
10918de8d7fSPeter Avalos 		fail++;
11018de8d7fSPeter Avalos 	else if (key->type != pktype)
11118de8d7fSPeter Avalos 		fail++;
11218de8d7fSPeter Avalos 	xfree(pkalg);
11318de8d7fSPeter Avalos 	xfree(pkblob);
11418de8d7fSPeter Avalos 
11518de8d7fSPeter Avalos 	/* client host name, handle trailing dot */
11618de8d7fSPeter Avalos 	p = buffer_get_string(&b, &len);
11718de8d7fSPeter Avalos 	debug2("valid_request: check expect chost %s got %s", host, p);
11818de8d7fSPeter Avalos 	if (strlen(host) != len - 1)
11918de8d7fSPeter Avalos 		fail++;
12018de8d7fSPeter Avalos 	else if (p[len - 1] != '.')
12118de8d7fSPeter Avalos 		fail++;
12218de8d7fSPeter Avalos 	else if (strncasecmp(host, p, len - 1) != 0)
12318de8d7fSPeter Avalos 		fail++;
12418de8d7fSPeter Avalos 	xfree(p);
12518de8d7fSPeter Avalos 
12618de8d7fSPeter Avalos 	/* local user */
12718de8d7fSPeter Avalos 	p = buffer_get_string(&b, NULL);
12818de8d7fSPeter Avalos 
12918de8d7fSPeter Avalos 	if (strcmp(pw->pw_name, p) != 0)
13018de8d7fSPeter Avalos 		fail++;
13118de8d7fSPeter Avalos 	xfree(p);
13218de8d7fSPeter Avalos 
13318de8d7fSPeter Avalos 	/* end of message */
13418de8d7fSPeter Avalos 	if (buffer_len(&b) != 0)
13518de8d7fSPeter Avalos 		fail++;
13618de8d7fSPeter Avalos 	buffer_free(&b);
13718de8d7fSPeter Avalos 
13818de8d7fSPeter Avalos 	debug3("valid_request: fail %d", fail);
13918de8d7fSPeter Avalos 
14018de8d7fSPeter Avalos 	if (fail && key != NULL)
14118de8d7fSPeter Avalos 		key_free(key);
14218de8d7fSPeter Avalos 	else
14318de8d7fSPeter Avalos 		*ret = key;
14418de8d7fSPeter Avalos 
14518de8d7fSPeter Avalos 	return (fail ? -1 : 0);
14618de8d7fSPeter Avalos }
14718de8d7fSPeter Avalos 
14818de8d7fSPeter Avalos int
14918de8d7fSPeter Avalos main(int argc, char **argv)
15018de8d7fSPeter Avalos {
15118de8d7fSPeter Avalos 	Buffer b;
15218de8d7fSPeter Avalos 	Options options;
15318de8d7fSPeter Avalos 	Key *keys[2], *key = NULL;
15418de8d7fSPeter Avalos 	struct passwd *pw;
15518de8d7fSPeter Avalos 	int key_fd[2], i, found, version = 2, fd;
15618de8d7fSPeter Avalos 	u_char *signature, *data;
15718de8d7fSPeter Avalos 	char *host;
15818de8d7fSPeter Avalos 	u_int slen, dlen;
15918de8d7fSPeter Avalos 	u_int32_t rnd[256];
16018de8d7fSPeter Avalos 
16118de8d7fSPeter Avalos 	/* Ensure that stdin and stdout are connected */
16218de8d7fSPeter Avalos 	if ((fd = open(_PATH_DEVNULL, O_RDWR)) < 2)
16318de8d7fSPeter Avalos 		exit(1);
16418de8d7fSPeter Avalos 	/* Leave /dev/null fd iff it is attached to stderr */
16518de8d7fSPeter Avalos 	if (fd > 2)
16618de8d7fSPeter Avalos 		close(fd);
16718de8d7fSPeter Avalos 
16818de8d7fSPeter Avalos 	key_fd[0] = open(_PATH_HOST_RSA_KEY_FILE, O_RDONLY);
16918de8d7fSPeter Avalos 	key_fd[1] = open(_PATH_HOST_DSA_KEY_FILE, O_RDONLY);
17018de8d7fSPeter Avalos 
17118de8d7fSPeter Avalos 	original_real_uid = getuid();	/* XXX readconf.c needs this */
17218de8d7fSPeter Avalos 	if ((pw = getpwuid(original_real_uid)) == NULL)
17318de8d7fSPeter Avalos 		fatal("getpwuid failed");
17418de8d7fSPeter Avalos 	pw = pwcopy(pw);
17518de8d7fSPeter Avalos 
17618de8d7fSPeter Avalos 	permanently_set_uid(pw);
17718de8d7fSPeter Avalos 
17818de8d7fSPeter Avalos 	init_rng();
17918de8d7fSPeter Avalos 	seed_rng();
18018de8d7fSPeter Avalos 	arc4random_stir();
18118de8d7fSPeter Avalos 
18218de8d7fSPeter Avalos #ifdef DEBUG_SSH_KEYSIGN
18318de8d7fSPeter Avalos 	log_init("ssh-keysign", SYSLOG_LEVEL_DEBUG3, SYSLOG_FACILITY_AUTH, 0);
18418de8d7fSPeter Avalos #endif
18518de8d7fSPeter Avalos 
18618de8d7fSPeter Avalos 	/* verify that ssh-keysign is enabled by the admin */
18718de8d7fSPeter Avalos 	initialize_options(&options);
18818de8d7fSPeter Avalos 	(void)read_config_file(_PATH_HOST_CONFIG_FILE, "", &options, 0);
18918de8d7fSPeter Avalos 	fill_default_options(&options);
19018de8d7fSPeter Avalos 	if (options.enable_ssh_keysign != 1)
19118de8d7fSPeter Avalos 		fatal("ssh-keysign not enabled in %s",
19218de8d7fSPeter Avalos 		    _PATH_HOST_CONFIG_FILE);
19318de8d7fSPeter Avalos 
19418de8d7fSPeter Avalos 	if (key_fd[0] == -1 && key_fd[1] == -1)
19518de8d7fSPeter Avalos 		fatal("could not open any host key");
19618de8d7fSPeter Avalos 
19718de8d7fSPeter Avalos 	SSLeay_add_all_algorithms();
19818de8d7fSPeter Avalos 	for (i = 0; i < 256; i++)
19918de8d7fSPeter Avalos 		rnd[i] = arc4random();
20018de8d7fSPeter Avalos 	RAND_seed(rnd, sizeof(rnd));
20118de8d7fSPeter Avalos 
20218de8d7fSPeter Avalos 	found = 0;
20318de8d7fSPeter Avalos 	for (i = 0; i < 2; i++) {
20418de8d7fSPeter Avalos 		keys[i] = NULL;
20518de8d7fSPeter Avalos 		if (key_fd[i] == -1)
20618de8d7fSPeter Avalos 			continue;
20718de8d7fSPeter Avalos 		keys[i] = key_load_private_pem(key_fd[i], KEY_UNSPEC,
20818de8d7fSPeter Avalos 		    NULL, NULL);
20918de8d7fSPeter Avalos 		close(key_fd[i]);
21018de8d7fSPeter Avalos 		if (keys[i] != NULL)
21118de8d7fSPeter Avalos 			found = 1;
21218de8d7fSPeter Avalos 	}
21318de8d7fSPeter Avalos 	if (!found)
21418de8d7fSPeter Avalos 		fatal("no hostkey found");
21518de8d7fSPeter Avalos 
21618de8d7fSPeter Avalos 	buffer_init(&b);
21718de8d7fSPeter Avalos 	if (ssh_msg_recv(STDIN_FILENO, &b) < 0)
21818de8d7fSPeter Avalos 		fatal("ssh_msg_recv failed");
21918de8d7fSPeter Avalos 	if (buffer_get_char(&b) != version)
22018de8d7fSPeter Avalos 		fatal("bad version");
22118de8d7fSPeter Avalos 	fd = buffer_get_int(&b);
22218de8d7fSPeter Avalos 	if ((fd == STDIN_FILENO) || (fd == STDOUT_FILENO))
22318de8d7fSPeter Avalos 		fatal("bad fd");
22418de8d7fSPeter Avalos 	if ((host = get_local_name(fd)) == NULL)
225*856ea928SPeter Avalos 		fatal("cannot get local name for fd");
22618de8d7fSPeter Avalos 
22718de8d7fSPeter Avalos 	data = buffer_get_string(&b, &dlen);
22818de8d7fSPeter Avalos 	if (valid_request(pw, host, &key, data, dlen) < 0)
22918de8d7fSPeter Avalos 		fatal("not a valid request");
23018de8d7fSPeter Avalos 	xfree(host);
23118de8d7fSPeter Avalos 
23218de8d7fSPeter Avalos 	found = 0;
23318de8d7fSPeter Avalos 	for (i = 0; i < 2; i++) {
23418de8d7fSPeter Avalos 		if (keys[i] != NULL &&
235*856ea928SPeter Avalos 		    key_equal_public(key, keys[i])) {
23618de8d7fSPeter Avalos 			found = 1;
23718de8d7fSPeter Avalos 			break;
23818de8d7fSPeter Avalos 		}
23918de8d7fSPeter Avalos 	}
24018de8d7fSPeter Avalos 	if (!found)
24118de8d7fSPeter Avalos 		fatal("no matching hostkey found");
24218de8d7fSPeter Avalos 
24318de8d7fSPeter Avalos 	if (key_sign(keys[i], &signature, &slen, data, dlen) != 0)
24418de8d7fSPeter Avalos 		fatal("key_sign failed");
24518de8d7fSPeter Avalos 	xfree(data);
24618de8d7fSPeter Avalos 
24718de8d7fSPeter Avalos 	/* send reply */
24818de8d7fSPeter Avalos 	buffer_clear(&b);
24918de8d7fSPeter Avalos 	buffer_put_string(&b, signature, slen);
25018de8d7fSPeter Avalos 	if (ssh_msg_send(STDOUT_FILENO, version, &b) == -1)
25118de8d7fSPeter Avalos 		fatal("ssh_msg_send failed");
25218de8d7fSPeter Avalos 
25318de8d7fSPeter Avalos 	return (0);
25418de8d7fSPeter Avalos }
255