1 /* $NetBSD: ssh-pkcs11-helper.c,v 1.10 2016/03/11 01:55:00 christos Exp $ */ 2 /* $OpenBSD: ssh-pkcs11-helper.c,v 1.12 2016/02/15 09:47:49 dtucker Exp $ */ 3 4 /* 5 * Copyright (c) 2010 Markus Friedl. All rights reserved. 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 #include "includes.h" 20 __RCSID("$NetBSD: ssh-pkcs11-helper.c,v 1.10 2016/03/11 01:55:00 christos Exp $"); 21 22 #include <sys/types.h> 23 #include <sys/queue.h> 24 #include <sys/time.h> 25 #include <sys/param.h> 26 27 #include <stdarg.h> 28 #include <string.h> 29 #include <unistd.h> 30 #include <errno.h> 31 32 #include "xmalloc.h" 33 #include "buffer.h" 34 #include "log.h" 35 #include "misc.h" 36 #include "key.h" 37 #include "authfd.h" 38 #include "ssh-pkcs11.h" 39 40 /* borrows code from sftp-server and ssh-agent */ 41 42 struct pkcs11_keyinfo { 43 Key *key; 44 char *providername; 45 TAILQ_ENTRY(pkcs11_keyinfo) next; 46 }; 47 48 TAILQ_HEAD(, pkcs11_keyinfo) pkcs11_keylist; 49 50 #define MAX_MSG_LENGTH 10240 /*XXX*/ 51 52 /* helper */ 53 #define get_int() buffer_get_int(&iqueue); 54 #define get_string(lenp) buffer_get_string(&iqueue, lenp); 55 56 /* input and output queue */ 57 Buffer iqueue; 58 Buffer oqueue; 59 60 static void 61 add_key(Key *k, char *name) 62 { 63 struct pkcs11_keyinfo *ki; 64 65 ki = xcalloc(1, sizeof(*ki)); 66 ki->providername = xstrdup(name); 67 ki->key = k; 68 TAILQ_INSERT_TAIL(&pkcs11_keylist, ki, next); 69 } 70 71 static void 72 del_keys_by_name(char *name) 73 { 74 struct pkcs11_keyinfo *ki, *nxt; 75 76 for (ki = TAILQ_FIRST(&pkcs11_keylist); ki; ki = nxt) { 77 nxt = TAILQ_NEXT(ki, next); 78 if (!strcmp(ki->providername, name)) { 79 TAILQ_REMOVE(&pkcs11_keylist, ki, next); 80 free(ki->providername); 81 key_free(ki->key); 82 free(ki); 83 } 84 } 85 } 86 87 /* lookup matching 'private' key */ 88 static Key * 89 lookup_key(Key *k) 90 { 91 struct pkcs11_keyinfo *ki; 92 93 TAILQ_FOREACH(ki, &pkcs11_keylist, next) { 94 debug("check %p %s", ki, ki->providername); 95 if (key_equal(k, ki->key)) 96 return (ki->key); 97 } 98 return (NULL); 99 } 100 101 static void 102 send_msg(Buffer *m) 103 { 104 int mlen = buffer_len(m); 105 106 buffer_put_int(&oqueue, mlen); 107 buffer_append(&oqueue, buffer_ptr(m), mlen); 108 buffer_consume(m, mlen); 109 } 110 111 static void 112 process_add(void) 113 { 114 char *name, *pin; 115 Key **keys; 116 int i, nkeys; 117 u_char *blob; 118 u_int blen; 119 Buffer msg; 120 121 buffer_init(&msg); 122 name = get_string(NULL); 123 pin = get_string(NULL); 124 if ((nkeys = pkcs11_add_provider(name, pin, &keys)) > 0) { 125 buffer_put_char(&msg, SSH2_AGENT_IDENTITIES_ANSWER); 126 buffer_put_int(&msg, nkeys); 127 for (i = 0; i < nkeys; i++) { 128 if (key_to_blob(keys[i], &blob, &blen) == 0) 129 continue; 130 buffer_put_string(&msg, blob, blen); 131 buffer_put_cstring(&msg, name); 132 free(blob); 133 add_key(keys[i], name); 134 } 135 free(keys); 136 } else { 137 buffer_put_char(&msg, SSH_AGENT_FAILURE); 138 } 139 free(pin); 140 free(name); 141 send_msg(&msg); 142 buffer_free(&msg); 143 } 144 145 static void 146 process_del(void) 147 { 148 char *name, *pin; 149 Buffer msg; 150 151 buffer_init(&msg); 152 name = get_string(NULL); 153 pin = get_string(NULL); 154 del_keys_by_name(name); 155 if (pkcs11_del_provider(name) == 0) 156 buffer_put_char(&msg, SSH_AGENT_SUCCESS); 157 else 158 buffer_put_char(&msg, SSH_AGENT_FAILURE); 159 free(pin); 160 free(name); 161 send_msg(&msg); 162 buffer_free(&msg); 163 } 164 165 static void 166 process_sign(void) 167 { 168 u_char *blob, *data, *signature = NULL; 169 u_int blen, dlen, slen = 0; 170 int ok = -1; 171 Key *key, *found; 172 Buffer msg; 173 174 blob = get_string(&blen); 175 data = get_string(&dlen); 176 (void)get_int(); /* XXX ignore flags */ 177 178 if ((key = key_from_blob(blob, blen)) != NULL) { 179 if ((found = lookup_key(key)) != NULL) { 180 #ifdef WITH_OPENSSL 181 int ret; 182 183 slen = RSA_size(key->rsa); 184 signature = xmalloc(slen); 185 if ((ret = RSA_private_encrypt(dlen, data, signature, 186 found->rsa, RSA_PKCS1_PADDING)) != -1) { 187 slen = ret; 188 ok = 0; 189 } 190 #endif /* WITH_OPENSSL */ 191 } 192 key_free(key); 193 } 194 buffer_init(&msg); 195 if (ok == 0) { 196 buffer_put_char(&msg, SSH2_AGENT_SIGN_RESPONSE); 197 buffer_put_string(&msg, signature, slen); 198 } else { 199 buffer_put_char(&msg, SSH_AGENT_FAILURE); 200 } 201 free(data); 202 free(blob); 203 free(signature); 204 send_msg(&msg); 205 buffer_free(&msg); 206 } 207 208 static void 209 process(void) 210 { 211 u_int msg_len; 212 u_int buf_len; 213 u_int consumed; 214 u_int type; 215 u_char *cp; 216 217 buf_len = buffer_len(&iqueue); 218 if (buf_len < 5) 219 return; /* Incomplete message. */ 220 cp = buffer_ptr(&iqueue); 221 msg_len = get_u32(cp); 222 if (msg_len > MAX_MSG_LENGTH) { 223 error("bad message len %d", msg_len); 224 cleanup_exit(11); 225 } 226 if (buf_len < msg_len + 4) 227 return; 228 buffer_consume(&iqueue, 4); 229 buf_len -= 4; 230 type = buffer_get_char(&iqueue); 231 switch (type) { 232 case SSH_AGENTC_ADD_SMARTCARD_KEY: 233 debug("process_add"); 234 process_add(); 235 break; 236 case SSH_AGENTC_REMOVE_SMARTCARD_KEY: 237 debug("process_del"); 238 process_del(); 239 break; 240 case SSH2_AGENTC_SIGN_REQUEST: 241 debug("process_sign"); 242 process_sign(); 243 break; 244 default: 245 error("Unknown message %d", type); 246 break; 247 } 248 /* discard the remaining bytes from the current packet */ 249 if (buf_len < buffer_len(&iqueue)) { 250 error("iqueue grew unexpectedly"); 251 cleanup_exit(255); 252 } 253 consumed = buf_len - buffer_len(&iqueue); 254 if (msg_len < consumed) { 255 error("msg_len %d < consumed %d", msg_len, consumed); 256 cleanup_exit(255); 257 } 258 if (msg_len > consumed) 259 buffer_consume(&iqueue, msg_len - consumed); 260 } 261 262 void 263 cleanup_exit(int i) 264 { 265 /* XXX */ 266 _exit(i); 267 } 268 269 int 270 main(int argc, char **argv) 271 { 272 fd_set *rset, *wset; 273 int in, out, max, log_stderr = 0; 274 ssize_t len, olen, set_size; 275 SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; 276 LogLevel log_level = SYSLOG_LEVEL_ERROR; 277 char buf[4*4096]; 278 extern char *__progname; 279 280 ssh_malloc_init(); /* must be called before any mallocs */ 281 TAILQ_INIT(&pkcs11_keylist); 282 pkcs11_init(0); 283 284 log_init(__progname, log_level, log_facility, log_stderr); 285 286 in = STDIN_FILENO; 287 out = STDOUT_FILENO; 288 289 max = 0; 290 if (in > max) 291 max = in; 292 if (out > max) 293 max = out; 294 295 buffer_init(&iqueue); 296 buffer_init(&oqueue); 297 298 set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask); 299 rset = xmalloc(set_size); 300 wset = xmalloc(set_size); 301 302 for (;;) { 303 memset(rset, 0, set_size); 304 memset(wset, 0, set_size); 305 306 /* 307 * Ensure that we can read a full buffer and handle 308 * the worst-case length packet it can generate, 309 * otherwise apply backpressure by stopping reads. 310 */ 311 if (buffer_check_alloc(&iqueue, sizeof(buf)) && 312 buffer_check_alloc(&oqueue, MAX_MSG_LENGTH)) 313 FD_SET(in, rset); 314 315 olen = buffer_len(&oqueue); 316 if (olen > 0) 317 FD_SET(out, wset); 318 319 if (select(max+1, rset, wset, NULL, NULL) < 0) { 320 if (errno == EINTR) 321 continue; 322 error("select: %s", strerror(errno)); 323 cleanup_exit(2); 324 } 325 326 /* copy stdin to iqueue */ 327 if (FD_ISSET(in, rset)) { 328 len = read(in, buf, sizeof buf); 329 if (len == 0) { 330 debug("read eof"); 331 cleanup_exit(0); 332 } else if (len < 0) { 333 error("read: %s", strerror(errno)); 334 cleanup_exit(1); 335 } else { 336 buffer_append(&iqueue, buf, len); 337 } 338 } 339 /* send oqueue to stdout */ 340 if (FD_ISSET(out, wset)) { 341 len = write(out, buffer_ptr(&oqueue), olen); 342 if (len < 0) { 343 error("write: %s", strerror(errno)); 344 cleanup_exit(1); 345 } else { 346 buffer_consume(&oqueue, len); 347 } 348 } 349 350 /* 351 * Process requests from client if we can fit the results 352 * into the output buffer, otherwise stop processing input 353 * and let the output queue drain. 354 */ 355 if (buffer_check_alloc(&oqueue, MAX_MSG_LENGTH)) 356 process(); 357 } 358 } 359