1/* $OpenBSD: ssh-keysign.c,v 1.36 2011/02/16 00:31:14 djm Exp $ */ 2/* 3 * Copyright (c) 2002 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" 27 28#include <fcntl.h> 29#ifdef HAVE_PATHS_H 30#include <paths.h> 31#endif 32#include <pwd.h> 33#include <stdarg.h> 34#include <stdlib.h> 35#include <string.h> 36#include <unistd.h> 37 38#include <openssl/evp.h> 39#include <openssl/rand.h> 40#include <openssl/rsa.h> 41 42#include "xmalloc.h" 43#include "log.h" 44#include "key.h" 45#include "ssh.h" 46#include "ssh2.h" 47#include "misc.h" 48#include "buffer.h" 49#include "authfile.h" 50#include "msg.h" 51#include "canohost.h" 52#include "pathnames.h" 53#include "readconf.h" 54#include "uidswap.h" 55 56/* XXX readconf.c needs these */ 57uid_t original_real_uid; 58 59extern char *__progname; 60 61static int 62valid_request(struct passwd *pw, char *host, Key **ret, u_char *data, 63 u_int datalen) 64{ 65 Buffer b; 66 Key *key = NULL; 67 u_char *pkblob; 68 u_int blen, len; 69 char *pkalg, *p; 70 int pktype, fail; 71 72 fail = 0; 73 74 buffer_init(&b); 75 buffer_append(&b, data, datalen); 76 77 /* session id, currently limited to SHA1 (20 bytes) or SHA256 (32) */ 78 p = buffer_get_string(&b, &len); 79 if (len != 20 && len != 32) 80 fail++; 81 xfree(p); 82 83 if (buffer_get_char(&b) != SSH2_MSG_USERAUTH_REQUEST) 84 fail++; 85 86 /* server user */ 87 buffer_skip_string(&b); 88 89 /* service */ 90 p = buffer_get_string(&b, NULL); 91 if (strcmp("ssh-connection", p) != 0) 92 fail++; 93 xfree(p); 94 95 /* method */ 96 p = buffer_get_string(&b, NULL); 97 if (strcmp("hostbased", p) != 0) 98 fail++; 99 xfree(p); 100 101 /* pubkey */ 102 pkalg = buffer_get_string(&b, NULL); 103 pkblob = buffer_get_string(&b, &blen); 104 105 pktype = key_type_from_name(pkalg); 106 if (pktype == KEY_UNSPEC) 107 fail++; 108 else if ((key = key_from_blob(pkblob, blen)) == NULL) 109 fail++; 110 else if (key->type != pktype) 111 fail++; 112 xfree(pkalg); 113 xfree(pkblob); 114 115 /* client host name, handle trailing dot */ 116 p = buffer_get_string(&b, &len); 117 debug2("valid_request: check expect chost %s got %s", host, p); 118 if (strlen(host) != len - 1) 119 fail++; 120 else if (p[len - 1] != '.') 121 fail++; 122 else if (strncasecmp(host, p, len - 1) != 0) 123 fail++; 124 xfree(p); 125 126 /* local user */ 127 p = buffer_get_string(&b, NULL); 128 129 if (strcmp(pw->pw_name, p) != 0) 130 fail++; 131 xfree(p); 132 133 /* end of message */ 134 if (buffer_len(&b) != 0) 135 fail++; 136 buffer_free(&b); 137 138 debug3("valid_request: fail %d", fail); 139 140 if (fail && key != NULL) 141 key_free(key); 142 else 143 *ret = key; 144 145 return (fail ? -1 : 0); 146} 147 148int 149main(int argc, char **argv) 150{ 151 Buffer b; 152 Options options; 153#define NUM_KEYTYPES 3 154 Key *keys[NUM_KEYTYPES], *key = NULL; 155 struct passwd *pw; 156 int key_fd[NUM_KEYTYPES], i, found, version = 2, fd; 157 u_char *signature, *data; 158 char *host; 159 u_int slen, dlen; 160 u_int32_t rnd[256]; 161 162 /* Ensure that stdin and stdout are connected */ 163 if ((fd = open(_PATH_DEVNULL, O_RDWR)) < 2) 164 exit(1); 165 /* Leave /dev/null fd iff it is attached to stderr */ 166 if (fd > 2) 167 close(fd); 168 169 i = 0; 170 key_fd[i++] = open(_PATH_HOST_DSA_KEY_FILE, O_RDONLY); 171 key_fd[i++] = open(_PATH_HOST_ECDSA_KEY_FILE, O_RDONLY); 172 key_fd[i++] = open(_PATH_HOST_RSA_KEY_FILE, O_RDONLY); 173 174 original_real_uid = getuid(); /* XXX readconf.c needs this */ 175 if ((pw = getpwuid(original_real_uid)) == NULL) 176 fatal("getpwuid failed"); 177 pw = pwcopy(pw); 178 179 permanently_set_uid(pw); 180 181 seed_rng(); 182 arc4random_stir(); 183 184#ifdef DEBUG_SSH_KEYSIGN 185 log_init("ssh-keysign", SYSLOG_LEVEL_DEBUG3, SYSLOG_FACILITY_AUTH, 0); 186#endif 187 188 /* verify that ssh-keysign is enabled by the admin */ 189 initialize_options(&options); 190 (void)read_config_file(_PATH_HOST_CONFIG_FILE, "", &options, 0); 191 fill_default_options(&options); 192 if (options.enable_ssh_keysign != 1) 193 fatal("ssh-keysign not enabled in %s", 194 _PATH_HOST_CONFIG_FILE); 195 196 for (i = found = 0; i < NUM_KEYTYPES; i++) { 197 if (key_fd[i] != -1) 198 found = 1; 199 } 200 if (found == 0) 201 fatal("could not open any host key"); 202 203 OpenSSL_add_all_algorithms(); 204 for (i = 0; i < 256; i++) 205 rnd[i] = arc4random(); 206 RAND_seed(rnd, sizeof(rnd)); 207 208 found = 0; 209 for (i = 0; i < NUM_KEYTYPES; i++) { 210 keys[i] = NULL; 211 if (key_fd[i] == -1) 212 continue; 213 keys[i] = key_load_private_pem(key_fd[i], KEY_UNSPEC, 214 NULL, NULL); 215 close(key_fd[i]); 216 if (keys[i] != NULL) 217 found = 1; 218 } 219 if (!found) 220 fatal("no hostkey found"); 221 222 buffer_init(&b); 223 if (ssh_msg_recv(STDIN_FILENO, &b) < 0) 224 fatal("ssh_msg_recv failed"); 225 if (buffer_get_char(&b) != version) 226 fatal("bad version"); 227 fd = buffer_get_int(&b); 228 if ((fd == STDIN_FILENO) || (fd == STDOUT_FILENO)) 229 fatal("bad fd"); 230 if ((host = get_local_name(fd)) == NULL) 231 fatal("cannot get local name for fd"); 232 233 data = buffer_get_string(&b, &dlen); 234 if (valid_request(pw, host, &key, data, dlen) < 0) 235 fatal("not a valid request"); 236 xfree(host); 237 238 found = 0; 239 for (i = 0; i < NUM_KEYTYPES; i++) { 240 if (keys[i] != NULL && 241 key_equal_public(key, keys[i])) { 242 found = 1; 243 break; 244 } 245 } 246 if (!found) 247 fatal("no matching hostkey found"); 248 249 if (key_sign(keys[i], &signature, &slen, data, dlen) != 0) 250 fatal("key_sign failed"); 251 xfree(data); 252 253 /* send reply */ 254 buffer_clear(&b); 255 buffer_put_string(&b, signature, slen); 256 if (ssh_msg_send(STDOUT_FILENO, version, &b) == -1) 257 fatal("ssh_msg_send failed"); 258 259 return (0); 260} 261