1*5411e769Sdjm /* $OpenBSD: ssh-pkcs11-helper.c,v 1.27 2024/08/15 00:51:51 djm Exp $ */ 241503fafSmarkus /* 341503fafSmarkus * Copyright (c) 2010 Markus Friedl. All rights reserved. 441503fafSmarkus * 541503fafSmarkus * Permission to use, copy, modify, and distribute this software for any 641503fafSmarkus * purpose with or without fee is hereby granted, provided that the above 741503fafSmarkus * copyright notice and this permission notice appear in all copies. 841503fafSmarkus * 941503fafSmarkus * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1041503fafSmarkus * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1141503fafSmarkus * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1241503fafSmarkus * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1341503fafSmarkus * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1441503fafSmarkus * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1541503fafSmarkus * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1641503fafSmarkus */ 1741503fafSmarkus 1841503fafSmarkus #include <sys/types.h> 19a12ef936Sdjm #include <sys/queue.h> 2041503fafSmarkus #include <sys/time.h> 2141503fafSmarkus 22d04a6061Sdjm #include <stdlib.h> 233fed7e05Sdjm #include <errno.h> 243fed7e05Sdjm #include <poll.h> 2541503fafSmarkus #include <stdarg.h> 2641503fafSmarkus #include <string.h> 2741503fafSmarkus #include <unistd.h> 2841503fafSmarkus 2941503fafSmarkus #include "xmalloc.h" 306c314464Smarkus #include "sshbuf.h" 3141503fafSmarkus #include "log.h" 3241503fafSmarkus #include "misc.h" 336c314464Smarkus #include "sshkey.h" 3441503fafSmarkus #include "authfd.h" 3541503fafSmarkus #include "ssh-pkcs11.h" 366c314464Smarkus #include "ssherr.h" 3741503fafSmarkus 381f96526fSdjm #ifdef WITH_OPENSSL 39*5411e769Sdjm #include <openssl/ec.h> 40*5411e769Sdjm #include <openssl/rsa.h> 411f96526fSdjm 4241503fafSmarkus /* borrows code from sftp-server and ssh-agent */ 4341503fafSmarkus 4441503fafSmarkus struct pkcs11_keyinfo { 4540a7db51Smarkus struct sshkey *key; 4644e54ccbSdjm char *providername, *label; 4741503fafSmarkus TAILQ_ENTRY(pkcs11_keyinfo) next; 4841503fafSmarkus }; 4941503fafSmarkus 5041503fafSmarkus TAILQ_HEAD(, pkcs11_keyinfo) pkcs11_keylist; 5141503fafSmarkus 5241503fafSmarkus #define MAX_MSG_LENGTH 10240 /*XXX*/ 5341503fafSmarkus 5441503fafSmarkus /* input and output queue */ 556c314464Smarkus struct sshbuf *iqueue; 566c314464Smarkus struct sshbuf *oqueue; 5741503fafSmarkus 5841503fafSmarkus static void 5944e54ccbSdjm add_key(struct sshkey *k, char *name, char *label) 6041503fafSmarkus { 6141503fafSmarkus struct pkcs11_keyinfo *ki; 6241503fafSmarkus 6341503fafSmarkus ki = xcalloc(1, sizeof(*ki)); 6441503fafSmarkus ki->providername = xstrdup(name); 6541503fafSmarkus ki->key = k; 6644e54ccbSdjm ki->label = xstrdup(label); 6741503fafSmarkus TAILQ_INSERT_TAIL(&pkcs11_keylist, ki, next); 6841503fafSmarkus } 6941503fafSmarkus 7041503fafSmarkus static void 7141503fafSmarkus del_keys_by_name(char *name) 7241503fafSmarkus { 7341503fafSmarkus struct pkcs11_keyinfo *ki, *nxt; 7441503fafSmarkus 7541503fafSmarkus for (ki = TAILQ_FIRST(&pkcs11_keylist); ki; ki = nxt) { 7641503fafSmarkus nxt = TAILQ_NEXT(ki, next); 7741503fafSmarkus if (!strcmp(ki->providername, name)) { 7841503fafSmarkus TAILQ_REMOVE(&pkcs11_keylist, ki, next); 790d40fefdSdjm free(ki->providername); 8044e54ccbSdjm free(ki->label); 816c314464Smarkus sshkey_free(ki->key); 8241503fafSmarkus free(ki); 8341503fafSmarkus } 8441503fafSmarkus } 8541503fafSmarkus } 8641503fafSmarkus 8741503fafSmarkus /* lookup matching 'private' key */ 8840a7db51Smarkus static struct sshkey * 8940a7db51Smarkus lookup_key(struct sshkey *k) 9041503fafSmarkus { 9141503fafSmarkus struct pkcs11_keyinfo *ki; 9241503fafSmarkus 9341503fafSmarkus TAILQ_FOREACH(ki, &pkcs11_keylist, next) { 94efa52ff8Sdjm debug("check %s %s %s", sshkey_type(ki->key), 95efa52ff8Sdjm ki->providername, ki->label); 966c314464Smarkus if (sshkey_equal(k, ki->key)) 9741503fafSmarkus return (ki->key); 9841503fafSmarkus } 9941503fafSmarkus return (NULL); 10041503fafSmarkus } 10141503fafSmarkus 10241503fafSmarkus static void 1036c314464Smarkus send_msg(struct sshbuf *m) 10441503fafSmarkus { 1056c314464Smarkus int r; 10641503fafSmarkus 1076c314464Smarkus if ((r = sshbuf_put_stringb(oqueue, m)) != 0) 10848e6b99dSdjm fatal_fr(r, "enqueue"); 10941503fafSmarkus } 11041503fafSmarkus 11141503fafSmarkus static void 11241503fafSmarkus process_add(void) 11341503fafSmarkus { 11441503fafSmarkus char *name, *pin; 11521f43f82Sdjm struct sshkey **keys = NULL; 1166c314464Smarkus int r, i, nkeys; 11741503fafSmarkus u_char *blob; 1186c314464Smarkus size_t blen; 1196c314464Smarkus struct sshbuf *msg; 12044e54ccbSdjm char **labels = NULL; 12141503fafSmarkus 1226c314464Smarkus if ((msg = sshbuf_new()) == NULL) 12348e6b99dSdjm fatal_f("sshbuf_new failed"); 1246c314464Smarkus if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 || 1256c314464Smarkus (r = sshbuf_get_cstring(iqueue, &pin, NULL)) != 0) 12648e6b99dSdjm fatal_fr(r, "parse"); 12744e54ccbSdjm if ((nkeys = pkcs11_add_provider(name, pin, &keys, &labels)) > 0) { 1286c314464Smarkus if ((r = sshbuf_put_u8(msg, 1296c314464Smarkus SSH2_AGENT_IDENTITIES_ANSWER)) != 0 || 1306c314464Smarkus (r = sshbuf_put_u32(msg, nkeys)) != 0) 13148e6b99dSdjm fatal_fr(r, "compose"); 13241503fafSmarkus for (i = 0; i < nkeys; i++) { 1336c314464Smarkus if ((r = sshkey_to_blob(keys[i], &blob, &blen)) != 0) { 13448e6b99dSdjm debug_fr(r, "encode key"); 135395d1200Sdjm continue; 1366c314464Smarkus } 1376c314464Smarkus if ((r = sshbuf_put_string(msg, blob, blen)) != 0 || 13844e54ccbSdjm (r = sshbuf_put_cstring(msg, labels[i])) != 0) 13948e6b99dSdjm fatal_fr(r, "compose key"); 1400d40fefdSdjm free(blob); 14144e54ccbSdjm add_key(keys[i], name, labels[i]); 14244e54ccbSdjm free(labels[i]); 14341503fafSmarkus } 14448e6b99dSdjm } else if ((r = sshbuf_put_u8(msg, SSH_AGENT_FAILURE)) != 0 || 14548e6b99dSdjm (r = sshbuf_put_u32(msg, -nkeys)) != 0) 14648e6b99dSdjm fatal_fr(r, "compose"); 14744e54ccbSdjm free(labels); 14844e54ccbSdjm free(keys); /* keys themselves are transferred to pkcs11_keylist */ 1490d40fefdSdjm free(pin); 1500d40fefdSdjm free(name); 1516c314464Smarkus send_msg(msg); 1526c314464Smarkus sshbuf_free(msg); 15341503fafSmarkus } 15441503fafSmarkus 15541503fafSmarkus static void 15641503fafSmarkus process_del(void) 15741503fafSmarkus { 15841503fafSmarkus char *name, *pin; 1596c314464Smarkus struct sshbuf *msg; 1606c314464Smarkus int r; 16141503fafSmarkus 1626c314464Smarkus if ((msg = sshbuf_new()) == NULL) 16348e6b99dSdjm fatal_f("sshbuf_new failed"); 1646c314464Smarkus if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 || 1656c314464Smarkus (r = sshbuf_get_cstring(iqueue, &pin, NULL)) != 0) 16648e6b99dSdjm fatal_fr(r, "parse"); 16741503fafSmarkus del_keys_by_name(name); 1686c314464Smarkus if ((r = sshbuf_put_u8(msg, pkcs11_del_provider(name) == 0 ? 1696c314464Smarkus SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE)) != 0) 17048e6b99dSdjm fatal_fr(r, "compose"); 1710d40fefdSdjm free(pin); 1720d40fefdSdjm free(name); 1736c314464Smarkus send_msg(msg); 1746c314464Smarkus sshbuf_free(msg); 17541503fafSmarkus } 17641503fafSmarkus 17741503fafSmarkus static void 17841503fafSmarkus process_sign(void) 17941503fafSmarkus { 18041503fafSmarkus u_char *blob, *data, *signature = NULL; 181*5411e769Sdjm size_t blen, dlen; 182*5411e769Sdjm u_int slen = 0; 183*5411e769Sdjm int len, r, ok = -1; 184*5411e769Sdjm struct sshkey *key = NULL, *found; 1856c314464Smarkus struct sshbuf *msg; 186*5411e769Sdjm RSA *rsa = NULL; 187*5411e769Sdjm EC_KEY *ecdsa = NULL; 18841503fafSmarkus 1896c314464Smarkus /* XXX support SHA2 signature flags */ 1906c314464Smarkus if ((r = sshbuf_get_string(iqueue, &blob, &blen)) != 0 || 1916c314464Smarkus (r = sshbuf_get_string(iqueue, &data, &dlen)) != 0 || 1926c314464Smarkus (r = sshbuf_get_u32(iqueue, NULL)) != 0) 19348e6b99dSdjm fatal_fr(r, "parse"); 19441503fafSmarkus 1956c314464Smarkus if ((r = sshkey_from_blob(blob, blen, &key)) != 0) 19648e6b99dSdjm fatal_fr(r, "decode key"); 197*5411e769Sdjm if ((found = lookup_key(key)) == NULL) 198*5411e769Sdjm goto reply; 199ea2d8289Sdjm 200*5411e769Sdjm /* XXX use pkey API properly for signing */ 201*5411e769Sdjm switch (key->type) { 202*5411e769Sdjm case KEY_RSA: 203*5411e769Sdjm if ((rsa = EVP_PKEY_get1_RSA(found->pkey)) == NULL) 204*5411e769Sdjm fatal_f("no RSA in pkey"); 205*5411e769Sdjm if ((len = RSA_size(rsa)) < 0) 206*5411e769Sdjm fatal_f("bad RSA length"); 207*5411e769Sdjm signature = xmalloc(len); 208*5411e769Sdjm if ((len = RSA_private_encrypt(dlen, data, signature, 209*5411e769Sdjm rsa, RSA_PKCS1_PADDING)) < 0) { 210*5411e769Sdjm error_f("RSA_private_encrypt failed"); 211*5411e769Sdjm goto reply; 212*5411e769Sdjm } 213*5411e769Sdjm slen = (u_int)len; 214*5411e769Sdjm break; 215*5411e769Sdjm case KEY_ECDSA: 216*5411e769Sdjm if ((ecdsa = EVP_PKEY_get1_EC_KEY(found->pkey)) == NULL) 217*5411e769Sdjm fatal_f("no ECDSA in pkey"); 218*5411e769Sdjm if ((len = ECDSA_size(ecdsa)) < 0) 219*5411e769Sdjm fatal_f("bad ECDSA length"); 220*5411e769Sdjm slen = (u_int)len; 22141503fafSmarkus signature = xmalloc(slen); 22221f43f82Sdjm /* "The parameter type is ignored." */ 223*5411e769Sdjm if (!ECDSA_sign(-1, data, dlen, signature, &slen, ecdsa)) { 224*5411e769Sdjm error_f("ECDSA_sign failed"); 225*5411e769Sdjm goto reply; 226*5411e769Sdjm } 227*5411e769Sdjm break; 228*5411e769Sdjm default: 229*5411e769Sdjm fatal_f("unsupported key type %d", key->type); 230*5411e769Sdjm } 231*5411e769Sdjm /* success */ 23221f43f82Sdjm ok = 0; 233*5411e769Sdjm reply: 2346c314464Smarkus if ((msg = sshbuf_new()) == NULL) 23548e6b99dSdjm fatal_f("sshbuf_new failed"); 23641503fafSmarkus if (ok == 0) { 2376c314464Smarkus if ((r = sshbuf_put_u8(msg, SSH2_AGENT_SIGN_RESPONSE)) != 0 || 2386c314464Smarkus (r = sshbuf_put_string(msg, signature, slen)) != 0) 23948e6b99dSdjm fatal_fr(r, "compose response"); 24041503fafSmarkus } else { 2416c314464Smarkus if ((r = sshbuf_put_u8(msg, SSH2_AGENT_FAILURE)) != 0) 24248e6b99dSdjm fatal_fr(r, "compose failure response"); 24341503fafSmarkus } 244*5411e769Sdjm sshkey_free(key); 245*5411e769Sdjm RSA_free(rsa); 246*5411e769Sdjm EC_KEY_free(ecdsa); 2470d40fefdSdjm free(data); 2480d40fefdSdjm free(blob); 2490d40fefdSdjm free(signature); 2506c314464Smarkus send_msg(msg); 2516c314464Smarkus sshbuf_free(msg); 25241503fafSmarkus } 25341503fafSmarkus 25441503fafSmarkus static void 25541503fafSmarkus process(void) 25641503fafSmarkus { 25741503fafSmarkus u_int msg_len; 25841503fafSmarkus u_int buf_len; 25941503fafSmarkus u_int consumed; 2606c314464Smarkus u_char type; 2616c314464Smarkus const u_char *cp; 2626c314464Smarkus int r; 26341503fafSmarkus 2646c314464Smarkus buf_len = sshbuf_len(iqueue); 26541503fafSmarkus if (buf_len < 5) 26641503fafSmarkus return; /* Incomplete message. */ 2676c314464Smarkus cp = sshbuf_ptr(iqueue); 26841503fafSmarkus msg_len = get_u32(cp); 26941503fafSmarkus if (msg_len > MAX_MSG_LENGTH) { 27041503fafSmarkus error("bad message len %d", msg_len); 27141503fafSmarkus cleanup_exit(11); 27241503fafSmarkus } 27341503fafSmarkus if (buf_len < msg_len + 4) 27441503fafSmarkus return; 2756c314464Smarkus if ((r = sshbuf_consume(iqueue, 4)) != 0 || 2766c314464Smarkus (r = sshbuf_get_u8(iqueue, &type)) != 0) 27748e6b99dSdjm fatal_fr(r, "parse type/len"); 27841503fafSmarkus buf_len -= 4; 27941503fafSmarkus switch (type) { 28041503fafSmarkus case SSH_AGENTC_ADD_SMARTCARD_KEY: 28141503fafSmarkus debug("process_add"); 28241503fafSmarkus process_add(); 28341503fafSmarkus break; 28441503fafSmarkus case SSH_AGENTC_REMOVE_SMARTCARD_KEY: 28541503fafSmarkus debug("process_del"); 28641503fafSmarkus process_del(); 28741503fafSmarkus break; 28841503fafSmarkus case SSH2_AGENTC_SIGN_REQUEST: 28941503fafSmarkus debug("process_sign"); 29041503fafSmarkus process_sign(); 29141503fafSmarkus break; 29241503fafSmarkus default: 29341503fafSmarkus error("Unknown message %d", type); 29441503fafSmarkus break; 29541503fafSmarkus } 29641503fafSmarkus /* discard the remaining bytes from the current packet */ 2976c314464Smarkus if (buf_len < sshbuf_len(iqueue)) { 29841503fafSmarkus error("iqueue grew unexpectedly"); 29941503fafSmarkus cleanup_exit(255); 30041503fafSmarkus } 3016c314464Smarkus consumed = buf_len - sshbuf_len(iqueue); 30241503fafSmarkus if (msg_len < consumed) { 30341503fafSmarkus error("msg_len %d < consumed %d", msg_len, consumed); 30441503fafSmarkus cleanup_exit(255); 30541503fafSmarkus } 3066c314464Smarkus if (msg_len > consumed) { 3076c314464Smarkus if ((r = sshbuf_consume(iqueue, msg_len - consumed)) != 0) 30848e6b99dSdjm fatal_fr(r, "consume"); 3096c314464Smarkus } 31041503fafSmarkus } 31141503fafSmarkus 31241503fafSmarkus void 31341503fafSmarkus cleanup_exit(int i) 31441503fafSmarkus { 31541503fafSmarkus /* XXX */ 31641503fafSmarkus _exit(i); 31741503fafSmarkus } 31841503fafSmarkus 3199f07e697Sdjm 32041503fafSmarkus int 32141503fafSmarkus main(int argc, char **argv) 32241503fafSmarkus { 3234bc33148Smarkus int r, ch, in, out, log_stderr = 0; 3243fed7e05Sdjm ssize_t len; 32541503fafSmarkus SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; 32641503fafSmarkus LogLevel log_level = SYSLOG_LEVEL_ERROR; 32741503fafSmarkus char buf[4*4096]; 328f2dd32a8Sderaadt extern char *__progname; 3293fed7e05Sdjm struct pollfd pfd[2]; 33041503fafSmarkus 33141503fafSmarkus TAILQ_INIT(&pkcs11_keylist); 33241503fafSmarkus 33341503fafSmarkus log_init(__progname, log_level, log_facility, log_stderr); 33441503fafSmarkus 3359f07e697Sdjm while ((ch = getopt(argc, argv, "v")) != -1) { 3369f07e697Sdjm switch (ch) { 3379f07e697Sdjm case 'v': 3389f07e697Sdjm log_stderr = 1; 3399f07e697Sdjm if (log_level == SYSLOG_LEVEL_ERROR) 3409f07e697Sdjm log_level = SYSLOG_LEVEL_DEBUG1; 3419f07e697Sdjm else if (log_level < SYSLOG_LEVEL_DEBUG3) 3429f07e697Sdjm log_level++; 3439f07e697Sdjm break; 3449f07e697Sdjm default: 3459f07e697Sdjm fprintf(stderr, "usage: %s [-v]\n", __progname); 3469f07e697Sdjm exit(1); 3479f07e697Sdjm } 3489f07e697Sdjm } 3499f07e697Sdjm 3509f07e697Sdjm log_init(__progname, log_level, log_facility, log_stderr); 3519f07e697Sdjm 3529f07e697Sdjm pkcs11_init(0); 35341503fafSmarkus in = STDIN_FILENO; 35441503fafSmarkus out = STDOUT_FILENO; 35541503fafSmarkus 3566c314464Smarkus if ((iqueue = sshbuf_new()) == NULL) 35748e6b99dSdjm fatal_f("sshbuf_new failed"); 3586c314464Smarkus if ((oqueue = sshbuf_new()) == NULL) 35948e6b99dSdjm fatal_f("sshbuf_new failed"); 36041503fafSmarkus 3613fed7e05Sdjm while (1) { 3623fed7e05Sdjm memset(pfd, 0, sizeof(pfd)); 3633fed7e05Sdjm pfd[0].fd = in; 3643fed7e05Sdjm pfd[1].fd = out; 36541503fafSmarkus 36641503fafSmarkus /* 36741503fafSmarkus * Ensure that we can read a full buffer and handle 36841503fafSmarkus * the worst-case length packet it can generate, 36941503fafSmarkus * otherwise apply backpressure by stopping reads. 37041503fafSmarkus */ 3716c314464Smarkus if ((r = sshbuf_check_reserve(iqueue, sizeof(buf))) == 0 && 3726c314464Smarkus (r = sshbuf_check_reserve(oqueue, MAX_MSG_LENGTH)) == 0) 3733fed7e05Sdjm pfd[0].events = POLLIN; 3746c314464Smarkus else if (r != SSH_ERR_NO_BUFFER_SPACE) 37548e6b99dSdjm fatal_fr(r, "reserve"); 37641503fafSmarkus 3773fed7e05Sdjm if (sshbuf_len(oqueue) > 0) 3783fed7e05Sdjm pfd[1].events = POLLOUT; 37941503fafSmarkus 3803fed7e05Sdjm if ((r = poll(pfd, 2, -1 /* INFTIM */)) <= 0) { 3813fed7e05Sdjm if (r == 0 || errno == EINTR) 38241503fafSmarkus continue; 3833fed7e05Sdjm fatal("poll: %s", strerror(errno)); 38441503fafSmarkus } 38541503fafSmarkus 38641503fafSmarkus /* copy stdin to iqueue */ 3873abf3b58Sdjm if ((pfd[0].revents & (POLLIN|POLLHUP|POLLERR)) != 0) { 38841503fafSmarkus len = read(in, buf, sizeof buf); 38941503fafSmarkus if (len == 0) { 39041503fafSmarkus debug("read eof"); 39141503fafSmarkus cleanup_exit(0); 39241503fafSmarkus } else if (len < 0) { 39341503fafSmarkus error("read: %s", strerror(errno)); 39441503fafSmarkus cleanup_exit(1); 39548e6b99dSdjm } else if ((r = sshbuf_put(iqueue, buf, len)) != 0) 39648e6b99dSdjm fatal_fr(r, "sshbuf_put"); 39741503fafSmarkus } 39841503fafSmarkus /* send oqueue to stdout */ 3993fed7e05Sdjm if ((pfd[1].revents & (POLLOUT|POLLHUP)) != 0) { 4003fed7e05Sdjm len = write(out, sshbuf_ptr(oqueue), 4013fed7e05Sdjm sshbuf_len(oqueue)); 40241503fafSmarkus if (len < 0) { 40341503fafSmarkus error("write: %s", strerror(errno)); 40441503fafSmarkus cleanup_exit(1); 40548e6b99dSdjm } else if ((r = sshbuf_consume(oqueue, len)) != 0) 40648e6b99dSdjm fatal_fr(r, "consume"); 40741503fafSmarkus } 40841503fafSmarkus 40941503fafSmarkus /* 41041503fafSmarkus * Process requests from client if we can fit the results 41141503fafSmarkus * into the output buffer, otherwise stop processing input 41241503fafSmarkus * and let the output queue drain. 41341503fafSmarkus */ 4146c314464Smarkus if ((r = sshbuf_check_reserve(oqueue, MAX_MSG_LENGTH)) == 0) 41541503fafSmarkus process(); 4166c314464Smarkus else if (r != SSH_ERR_NO_BUFFER_SPACE) 41748e6b99dSdjm fatal_fr(r, "reserve"); 41841503fafSmarkus } 41941503fafSmarkus } 4201f96526fSdjm 4211f96526fSdjm #else /* WITH_OPENSSL */ 4221f96526fSdjm void 4231f96526fSdjm cleanup_exit(int i) 4241f96526fSdjm { 4251f96526fSdjm _exit(i); 4261f96526fSdjm } 4271f96526fSdjm 4281f96526fSdjm int 4291f96526fSdjm main(int argc, char **argv) 4301f96526fSdjm { 4311f96526fSdjm fprintf(stderr, "PKCS#11 code is not enabled\n"); 4321f96526fSdjm return 1; 4331f96526fSdjm } 4341f96526fSdjm #endif /* WITH_OPENSSL */ 435