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