1 /* $OpenBSD: ssh-keysign.c,v 1.29 2006/08/03 03:34:42 deraadt 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 Key *keys[2], *key; 149 struct passwd *pw; 150 int key_fd[2], i, found, version = 2, fd; 151 u_char *signature, *data; 152 char *host; 153 u_int slen, dlen; 154 u_int32_t rnd[256]; 155 156 /* Ensure that stdin and stdout are connected */ 157 if ((fd = open(_PATH_DEVNULL, O_RDWR)) < 2) 158 exit(1); 159 /* Leave /dev/null fd iff it is attached to stderr */ 160 if (fd > 2) 161 close(fd); 162 163 key_fd[0] = open(_PATH_HOST_RSA_KEY_FILE, O_RDONLY); 164 key_fd[1] = open(_PATH_HOST_DSA_KEY_FILE, O_RDONLY); 165 166 original_real_uid = getuid(); /* XXX readconf.c needs this */ 167 if ((pw = getpwuid(original_real_uid)) == NULL) 168 fatal("getpwuid failed"); 169 pw = pwcopy(pw); 170 171 permanently_set_uid(pw); 172 173 #ifdef DEBUG_SSH_KEYSIGN 174 log_init("ssh-keysign", SYSLOG_LEVEL_DEBUG3, SYSLOG_FACILITY_AUTH, 0); 175 #endif 176 177 /* verify that ssh-keysign is enabled by the admin */ 178 initialize_options(&options); 179 (void)read_config_file(_PATH_HOST_CONFIG_FILE, "", &options, 0); 180 fill_default_options(&options); 181 if (options.enable_ssh_keysign != 1) 182 fatal("ssh-keysign not enabled in %s", 183 _PATH_HOST_CONFIG_FILE); 184 185 if (key_fd[0] == -1 && key_fd[1] == -1) 186 fatal("could not open any host key"); 187 188 SSLeay_add_all_algorithms(); 189 for (i = 0; i < 256; i++) 190 rnd[i] = arc4random(); 191 RAND_seed(rnd, sizeof(rnd)); 192 193 found = 0; 194 for (i = 0; i < 2; i++) { 195 keys[i] = NULL; 196 if (key_fd[i] == -1) 197 continue; 198 keys[i] = key_load_private_pem(key_fd[i], KEY_UNSPEC, 199 NULL, NULL); 200 close(key_fd[i]); 201 if (keys[i] != NULL) 202 found = 1; 203 } 204 if (!found) 205 fatal("no hostkey found"); 206 207 buffer_init(&b); 208 if (ssh_msg_recv(STDIN_FILENO, &b) < 0) 209 fatal("ssh_msg_recv failed"); 210 if (buffer_get_char(&b) != version) 211 fatal("bad version"); 212 fd = buffer_get_int(&b); 213 if ((fd == STDIN_FILENO) || (fd == STDOUT_FILENO)) 214 fatal("bad fd"); 215 if ((host = get_local_name(fd)) == NULL) 216 fatal("cannot get sockname for fd"); 217 218 data = buffer_get_string(&b, &dlen); 219 if (valid_request(pw, host, &key, data, dlen) < 0) 220 fatal("not a valid request"); 221 xfree(host); 222 223 found = 0; 224 for (i = 0; i < 2; i++) { 225 if (keys[i] != NULL && 226 key_equal(key, keys[i])) { 227 found = 1; 228 break; 229 } 230 } 231 if (!found) 232 fatal("no matching hostkey found"); 233 234 if (key_sign(keys[i], &signature, &slen, data, dlen) != 0) 235 fatal("key_sign failed"); 236 xfree(data); 237 238 /* send reply */ 239 buffer_clear(&b); 240 buffer_put_string(&b, signature, slen); 241 if (ssh_msg_send(STDOUT_FILENO, version, &b) == -1) 242 fatal("ssh_msg_send failed"); 243 244 return (0); 245 } 246