# HG changeset patch # User kais # Date 1131850685 28800 # Node ID 64b2a371a6bdec7cd3e41c62d783dd06ac84cb2a # Parent 8bc35ca89c2fd2bae11581e6b02766610288212e PSARC/2005/625 Greyhound - Solaris Kernel SSL proxy 4931229 Kernel-level SSL proxy diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/cmd/cmd-inet/usr.sbin/Makefile --- a/usr/src/cmd/cmd-inet/usr.sbin/Makefile Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/cmd/cmd-inet/usr.sbin/Makefile Sat Nov 12 18:58:05 2005 -0800 @@ -68,12 +68,13 @@ SRCS= $(PROGSRCS) $(OTHERSRC) SUBDIRS= bootconfchk htable ifconfig in.ftpd in.routed \ - in.talkd inetadm inetconv ipqosconf mipagentconfig \ - mipagentstat ping snoop sppptun traceroute + in.talkd inetadm inetconv ipqosconf kssl/kssladm \ + kssl/ksslcfg mipagentconfig mipagentstat ping snoop \ + sppptun traceroute MSGSUBDIRS= bootconfchk htable ifconfig in.ftpd in.routed in.talkd \ - inetadm inetconv ipqosconf mipagentconfig mipagentstat \ - sppptun snoop + inetadm inetconv ipqosconf kssl/ksslcfg mipagentconfig \ + mipagentstat sppptun snoop # As programs get lint-clean, add them here and to the 'lint' target. # Eventually this hack should go away, and all in PROG should be diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/cmd/cmd-inet/usr.sbin/kssl/kssladm/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/cmd-inet/usr.sbin/kssl/kssladm/Makefile Sat Nov 12 18:58:05 2005 -0800 @@ -0,0 +1,73 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# cmd/cmd-inet/usr.sbin/kssl/kssladm/Makefile +# + +PROG= kssladm + +include $(SRC)/cmd/Makefile.cmd +include $(SRC)/lib/openssl/Makefile.openssl + +OBJS = \ + kssladm.o \ + kssladm_create.o \ + kssladm_delete.o \ + openssl_util.o + +SRCS = $(OBJS:%.o=%.c) + +ROOTUSRLIBPROG = $(PROG:%=$(ROOTLIB)/%) + +.KEEP_STATE: + +CFLAGS += $(CCVERBOSE) + +CPPFLAGS += $(OPENSSL_CPPFLAGS) +DYNFLAGS += $(OPENSSL_DYNFLAGS) +LDFLAGS += $(OPENSSL_LDFLAGS) +LINTFLAGS += $(OPENSSL_LDFLAGS) + +LDLIBS += -lnsl -lpkcs11 -lcrypto -lcryptoutil + +all: $(PROG) + +$(PROG): $(OBJS) + $(LINK.c) $(OBJS) -o $@ $(LDLIBS) $(DYNFLAGS) + $(POST_PROCESS) + +install: all $(ROOTUSRLIBPROG) + +clean: + $(RM) $(OBJS) + +check: + $(CSTYLE) -pP $(SRCS) + +lint: lint_SRCS + +include ../../../../Makefile.targ diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/cmd/cmd-inet/usr.sbin/kssl/kssladm/kssladm.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/cmd-inet/usr.sbin/kssl/kssladm/kssladm.c Sat Nov 12 18:58:05 2005 -0800 @@ -0,0 +1,169 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "kssladm.h" + + +/* + * kssladm(1M) + * + * Command to manage the entries in kernel SSL proxy table. This is + * a private command called indirectly from ksslcfg(1M). + */ + +boolean_t verbose = B_FALSE; + +static void +usage_all(void) +{ + (void) fprintf(stderr, "Usage:\n"); + usage_create(B_FALSE); + usage_delete(B_FALSE); +} + +int +main(int argc, char **argv) +{ + int rv = SUCCESS; + + if (argc < 2) { + usage_all(); + return (SMF_EXIT_ERR_CONFIG); + } + + if (strcmp(argv[1], "create") == 0) { + rv = do_create(argc, argv); + } else if (strcmp(argv[1], "delete") == 0) { + rv = do_delete(argc, argv); + } else { + (void) fprintf(stderr, "Unknown sub-command: %s\n", argv[1]); + usage_all(); + rv = SMF_EXIT_ERR_CONFIG; + } + + return (rv); +} + + +/* + * Read a passphrase from the file into the supplied buffer. + * A space character and the characters that follow + * the space character will be ignored. + * Return 0 when no valid passphrase was found in the file. + */ +static int +read_pass_from_file(const char *filename, char *buffer, size_t bufsize) +{ + char *line; + char *p; + FILE *fp; + + fp = fopen(filename, "r"); + if (fp == NULL) { + (void) fprintf(stderr, + "Unable to open password file for reading"); + return (1); + } + + line = fgets(buffer, bufsize, fp); + (void) fclose(fp); + if (line == NULL) { + return (0); + } + + for (p = buffer; *p != '\0'; p++) { + if (isspace(*p)) { + *p = '\0'; + break; + } + } + + return (p - buffer); +} + + +int +get_passphrase(const char *password_file, char *buf, int buf_size) +{ + if (password_file == NULL) { + char *passphrase = getpassphrase("Enter passphrase: "); + if (passphrase) { + return (strlcpy(buf, passphrase, buf_size)); + } + + return (0); + } + + return (read_pass_from_file(password_file, buf, buf_size)); +} + + +int +kssl_send_command(char *buf, int cmd) +{ + int ksslfd; + int rv; + + ksslfd = open("/dev/kssl", O_RDWR); + if (ksslfd < 0) { + perror("Cannot open /dev/kssl"); + return (EBADF); + } + + if ((rv = ioctl(ksslfd, cmd, buf)) < 0) { + switch (errno) { + case EEXIST: + (void) fprintf(stderr, + "Error: Can not create a INADDR_ANY instance" + " while another instance exists.\n"); + break; + case EADDRINUSE: + (void) fprintf(stderr, + "Error: Another instance with the same" + " proxy port exists.\n"); + break; + default: + perror("ioctl failure"); + break; + } + } + + (void) close(ksslfd); + + return (rv); +} diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/cmd/cmd-inet/usr.sbin/kssl/kssladm/kssladm.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/cmd-inet/usr.sbin/kssl/kssladm/kssladm.h Sat Nov 12 18:58:05 2005 -0800 @@ -0,0 +1,68 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _KSSLADM_H +#define _KSSLADM_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Common routines and variables used by kssladm files. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#define SUCCESS 0 +#define FAILURE 1 +#define ERROR_USAGE 2 + +extern boolean_t verbose; + +extern int do_create(int argc, char *argv[]); +extern int do_delete(int argc, char *argv[]); +extern void usage_create(boolean_t do_print); +extern void usage_delete(boolean_t do_print); + +extern uchar_t *get_modulus(uchar_t *ber_buf, int buflen, int *modlen); +extern RSA *PEM_get_rsa_key(const char *filename, char *passphrase); +extern uchar_t *PEM_get_cert(const char *filename, char *passphrase, + int *cert_size); +extern int PKCS12_get_rsa_key_cert(const char *filename, const char *password, + RSA **rsa, uchar_t **cert, int *cert_size); +extern int get_passphrase(const char *password_file, char *buf, int buf_size); +extern int kssl_send_command(char *buf, int cmd); +extern int parse_and_set_addr(char *arg1, char *arg2, struct sockaddr_in *addr); + +#ifdef __cplusplus +} +#endif + +#endif /* _KSSLADM_H */ diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/cmd/cmd-inet/usr.sbin/kssl/kssladm/kssladm_create.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/cmd-inet/usr.sbin/kssl/kssladm/kssladm_create.c Sat Nov 12 18:58:05 2005 -0800 @@ -0,0 +1,972 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include /* hostent */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "kssladm.h" + +void +usage_create(boolean_t do_print) +{ + if (do_print) + (void) fprintf(stderr, "Usage:\n"); + (void) fprintf(stderr, "kssladm create" + " -f pkcs11 [-d softtoken_directory] -T " + " -C -x " + " [options] [] []\n"); + + (void) fprintf(stderr, "kssladm create" + " -f pkcs12 -i -x " + " [options] [] []\n"); + + (void) fprintf(stderr, "kssladm create" + " -f pem -i -x " + " [options] [] []\n"); + + (void) fprintf(stderr, "options are:\n" + "\t[-c ]\n" + "\t[-p ]\n" + "\t[-t ]\n" + "\t[-z ]\n" + "\t[-v]\n"); +} + +static uchar_t * +get_cert_val(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE cert_obj, int *len) +{ + CK_RV rv; + uchar_t *buf; + CK_ATTRIBUTE cert_attrs[] = {{CKA_VALUE, NULL, 0}}; + + /* the certs ... */ + rv = C_GetAttributeValue(sess, cert_obj, cert_attrs, 1); + if (rv != CKR_OK) { + (void) fprintf(stderr, "Cannot get cert size." + " error = %s\n", pkcs11_strerror(rv)); + return (NULL); + } + + buf = malloc(cert_attrs[0].ulValueLen); + if (buf == NULL) + return (NULL); + cert_attrs[0].pValue = buf; + + rv = C_GetAttributeValue(sess, cert_obj, cert_attrs, 1); + if (rv != CKR_OK) { + (void) fprintf(stderr, "Cannot get cert value." + " error = %s\n", pkcs11_strerror(rv)); + free(buf); + return (NULL); + } + + *len = cert_attrs[0].ulValueLen; + return (buf); +} + +#define REQ_ATTR_CNT 2 +#define OPT_ATTR_CNT 6 +#define MAX_ATTR_CNT (REQ_ATTR_CNT + OPT_ATTR_CNT) + +/* + * Everything is allocated in one single contiguous buffer. + * The layout is the following: + * . the kssl_params_t structure + * . the array of sizes of the certificates, (value of sc_sizes_offset) + * . the array of key attribute structs, (value of ck_attrs) + * . the certificates values (values of sc_certs[i]) + * . the key attributes values (values of ck_attrs[i].ck_value); + * + * The address of the certs and key attributes values are offsets + * from the beginning of the big buffer. + */ +static kssl_params_t * +pkcs11_to_kssl(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE privkey_obj, + CK_OBJECT_HANDLE cert_obj, int *paramsize) +{ + int i; + CK_RV rv; + CK_ATTRIBUTE privkey_attrs[MAX_ATTR_CNT] = { + {CKA_MODULUS, NULL_PTR, 0}, + {CKA_PRIVATE_EXPONENT, NULL_PTR, 0} + }; + CK_ATTRIBUTE privkey_opt_attrs[OPT_ATTR_CNT] = { + {CKA_PUBLIC_EXPONENT, NULL_PTR, 0}, + {CKA_PRIME_1, NULL_PTR, 0}, + {CKA_PRIME_2, NULL_PTR, 0}, + {CKA_EXPONENT_1, NULL_PTR, 0}, + {CKA_EXPONENT_2, NULL_PTR, 0}, + {CKA_COEFFICIENT, NULL_PTR, 0} + }; + CK_ATTRIBUTE cert_attrs[] = { {CKA_VALUE, NULL, 0} }; + kssl_object_attribute_t kssl_attrs[MAX_ATTR_CNT]; + kssl_params_t *kssl_params; + kssl_key_t *key; + char *buf; + uint32_t cert_size, bufsize; + int attr_cnt; + + /* the certs ... */ + rv = C_GetAttributeValue(sess, cert_obj, cert_attrs, 1); + if (rv != CKR_OK) { + (void) fprintf(stderr, "Cannot get cert size." + " error = %s\n", pkcs11_strerror(rv)); + return (NULL); + } + + /* Get the sizes */ + bufsize = sizeof (kssl_params_t); + cert_size = (uint32_t)cert_attrs[0].ulValueLen; + bufsize += cert_size + sizeof (uint32_t); + + /* and the required key attributes */ + rv = C_GetAttributeValue(sess, privkey_obj, privkey_attrs, + REQ_ATTR_CNT); + if (rv != CKR_OK) { + (void) fprintf(stderr, + "Cannot get private key object attributes. error = %s\n", + pkcs11_strerror(rv)); + return (NULL); + } + for (i = 0; i < REQ_ATTR_CNT; i++) { + bufsize += sizeof (crypto_object_attribute_t) + + privkey_attrs[i].ulValueLen; + } + attr_cnt = REQ_ATTR_CNT; + + /* + * Get the optional key attributes. The return values could be + * CKR_ATTRIBUTE_TYPE_INVALID with ulValueLen set to -1 OR + * CKR_OK with ulValueLen set to 0. The latter is done by + * soft token and seems dubious. + */ + rv = C_GetAttributeValue(sess, privkey_obj, privkey_opt_attrs, + OPT_ATTR_CNT); + if (rv != CKR_OK && rv != CKR_ATTRIBUTE_TYPE_INVALID) { + (void) fprintf(stderr, + "Cannot get private key object attributes. error = %s\n", + pkcs11_strerror(rv)); + return (NULL); + } + for (i = 0; i < OPT_ATTR_CNT; i++) { + if (privkey_opt_attrs[i].ulValueLen == (CK_ULONG)-1 || + privkey_opt_attrs[i].ulValueLen == 0) + continue; + /* Structure copy */ + privkey_attrs[attr_cnt] = privkey_opt_attrs[i]; + bufsize += sizeof (crypto_object_attribute_t) + + privkey_opt_attrs[i].ulValueLen; + attr_cnt++; + } + + /* Now the big memory allocation */ + if ((buf = calloc(bufsize, 1)) == NULL) { + (void) fprintf(stderr, + "Cannot allocate memory for the kssl_params " + "and values\n"); + return (NULL); + } + + /* LINTED */ + kssl_params = (kssl_params_t *)buf; + + buf = (char *)(kssl_params + 1); + + kssl_params->kssl_certs.sc_count = 1; + bcopy(&cert_size, buf, sizeof (uint32_t)); + kssl_params->kssl_certs.sc_sizes_offset = buf - (char *)kssl_params; + buf += sizeof (uint32_t); + + /* the keys attributes structs array */ + key = &kssl_params->kssl_privkey; + key->ks_format = CRYPTO_KEY_ATTR_LIST; + key->ks_count = attr_cnt; + key->ks_attrs_offset = buf - (char *)kssl_params; + buf += attr_cnt * sizeof (kssl_object_attribute_t); + + /* now the certs values */ + cert_attrs[0].pValue = buf; + kssl_params->kssl_certs.sc_certs_offset = buf - (char *)kssl_params; + buf += cert_attrs[0].ulValueLen; + + rv = C_GetAttributeValue(sess, cert_obj, cert_attrs, 1); + if (rv != CKR_OK) { + (void) fprintf(stderr, "Cannot get cert value." + " error = %s\n", pkcs11_strerror(rv)); + return (NULL); + } + + /* then the attributes values */ + for (i = 0; i < attr_cnt; i++) { + privkey_attrs[i].pValue = buf; + /* + * We assume the attribute types in the kernel are + * the same as the PKCS #11 values. + */ + kssl_attrs[i].ka_type = privkey_attrs[i].type; + kssl_attrs[i].ka_value_offset = buf - (char *)kssl_params; + + kssl_attrs[i].ka_value_len = privkey_attrs[i].ulValueLen; + + buf += privkey_attrs[i].ulValueLen; + } + /* then the key attributes values */ + rv = C_GetAttributeValue(sess, privkey_obj, privkey_attrs, attr_cnt); + if (rv != CKR_OK) { + (void) fprintf(stderr, + "Cannot get private key object attributes." + " error = %s\n", pkcs11_strerror(rv)); + return (NULL); + } + + bcopy(kssl_attrs, ((char *)kssl_params) + key->ks_attrs_offset, + attr_cnt * sizeof (kssl_object_attribute_t)); + + *paramsize = bufsize; + return (kssl_params); +} + +#define max_num_cert 32 + +kssl_params_t * +load_from_pkcs11(const char *token_label, const char *password_file, + const char *certname, int *bufsize) +{ + static CK_BBOOL true = TRUE; + static CK_BBOOL false = FALSE; + + CK_RV rv; + CK_SLOT_ID slot; + CK_SLOT_ID_PTR pk11_slots; + CK_ULONG slotcnt = 10; + CK_TOKEN_INFO token_info; + CK_SESSION_HANDLE sess; + static CK_OBJECT_CLASS cert_class = CKO_CERTIFICATE; + static CK_CERTIFICATE_TYPE cert_type = CKC_X_509; + CK_ATTRIBUTE cert_tmpl[4] = { + {CKA_TOKEN, &true, sizeof (true)}, + {CKA_LABEL, NULL, 0}, + {CKA_CLASS, &cert_class, sizeof (cert_class)}, + {CKA_CERTIFICATE_TYPE, &cert_type, sizeof (cert_type)} + }; + CK_ULONG cert_tmpl_count = 4, cert_obj_count = 1; + CK_OBJECT_HANDLE cert_obj, privkey_obj; + CK_OBJECT_HANDLE cert_objs[max_num_cert]; + static CK_OBJECT_CLASS privkey_class = CKO_PRIVATE_KEY; + static CK_KEY_TYPE privkey_type = CKK_RSA; + CK_ATTRIBUTE privkey_tmpl[] = { + {CKA_MODULUS, NULL, 0}, + {CKA_TOKEN, &true, sizeof (true)}, + {CKA_CLASS, &privkey_class, sizeof (privkey_class)}, + {CKA_KEY_TYPE, &privkey_type, sizeof (privkey_type)} + }; + CK_ULONG privkey_tmpl_count = 4, privkey_obj_count = 1; + static CK_BYTE modulus[1024]; + CK_ATTRIBUTE privkey_attrs[1] = { + {CKA_MODULUS, modulus, sizeof (modulus)}, + }; + boolean_t bingo = B_FALSE; + int blen, mlen; + uchar_t *mval, *ber_buf; + char token_label_padded[sizeof (token_info.label) + 1]; + + (void) snprintf(token_label_padded, sizeof (token_label_padded), + "%-32s", token_label); + + rv = C_Initialize(NULL_PTR); + + if ((rv != CKR_OK) && (rv != CKR_CRYPTOKI_ALREADY_INITIALIZED)) { + (void) fprintf(stderr, + "Cannot initialize PKCS#11. error = %s\n", + pkcs11_strerror(rv)); + return (NULL); + } + + /* Get slot count */ + rv = C_GetSlotList(1, NULL_PTR, &slotcnt); + if (rv != CKR_OK || slotcnt == 0) { + (void) fprintf(stderr, + "Cannot get PKCS#11 slot list. error = %s\n", + pkcs11_strerror(rv)); + return (NULL); + } + + pk11_slots = calloc(slotcnt, sizeof (CK_SLOT_ID)); + if (pk11_slots == NULL) { + (void) fprintf(stderr, + "Cannot get memory for %ld slots\n", slotcnt); + return (NULL); + } + + rv = C_GetSlotList(1, pk11_slots, &slotcnt); + if (rv != CKR_OK) { + (void) fprintf(stderr, + "Cannot get PKCS#11 slot list. error = %s\n", + pkcs11_strerror(rv)); + return (NULL); + } + + if (verbose) + (void) printf("Found %ld slots\n", slotcnt); + + /* Search the token that matches the label */ + while (slotcnt > 0) { + rv = C_GetTokenInfo(pk11_slots[--slotcnt], &token_info); + if (rv != CKR_OK) + continue; + + if (verbose) + (void) printf("slot [%ld] = %s\n", + slotcnt, token_info.label); + if (memcmp(token_label_padded, token_info.label, + sizeof (token_info.label)) == 0) { + bingo = B_TRUE; + slot = pk11_slots[slotcnt]; + break; + } + if (verbose) { + token_info.label[31] = '\0'; + (void) printf("found slot [%s]\n", token_info.label); + } + } + + if (!bingo) { + (void) fprintf(stderr, "no matching PKCS#11 token found\n"); + return (NULL); + } + + rv = C_OpenSession(slot, CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR, + &sess); + if (rv != CKR_OK) { + (void) fprintf(stderr, "Cannot open session. error = %s\n", + pkcs11_strerror(rv)); + return (NULL); + } + + cert_tmpl[1].pValue = (CK_VOID_PTR) certname; + cert_tmpl[1].ulValueLen = strlen(certname); + + rv = C_FindObjectsInit(sess, cert_tmpl, cert_tmpl_count); + if (rv != CKR_OK) { + (void) fprintf(stderr, + "Cannot intialize cert search." + " error = %s\n", pkcs11_strerror(rv)); + return (NULL); + } + + rv = C_FindObjects(sess, cert_objs, + (certname == NULL ? 1 : max_num_cert), &cert_obj_count); + if (rv != CKR_OK) { + (void) fprintf(stderr, + "Cannot retrieve cert object. error = %s\n", + pkcs11_strerror(rv)); + return (NULL); + } + + /* Who cares if this fails! */ + (void) C_FindObjectsFinal(sess); + if (verbose) + (void) printf("found %ld certificates\n", cert_obj_count); + + if (cert_obj_count == 0) { + (void) fprintf(stderr, "\"%s\" not found.\n", certname); + (void) fprintf(stderr, "no certs. bye.\n"); + return (NULL); + } + + cert_obj = cert_objs[0]; + + /* Get the modulus value from the certificate */ + ber_buf = get_cert_val(sess, cert_obj, &blen); + if (ber_buf == NULL) { + (void) fprintf(stderr, + "Cannot get certificate data for \"%s\".\n", certname); + return (NULL); + } + + mval = get_modulus(ber_buf, blen, &mlen); + if (mval == NULL) { + (void) fprintf(stderr, + "Cannot get Modulus in certificate \"%s\".\n", certname); + return (NULL); + } + + /* Now get the private key */ + + /* Gotta authenticate first if login is required. */ + if (token_info.flags & CKF_LOGIN_REQUIRED) { + char passphrase[1024]; + CK_ULONG ulPinLen; + + ulPinLen = get_passphrase( + password_file, passphrase, sizeof (passphrase)); + if (ulPinLen == 0) { + (void) fprintf(stderr, "Unable to read passphrase"); + return (NULL); + } + + rv = C_Login(sess, CKU_USER, (CK_UTF8CHAR_PTR)passphrase, + ulPinLen); + if (rv != CKR_OK) { + (void) fprintf(stderr, "Cannot login to the token." + " error = %s\n", pkcs11_strerror(rv)); + return (NULL); + } + } + + privkey_tmpl[0].pValue = mval; + privkey_tmpl[0].ulValueLen = mlen; + + rv = C_FindObjectsInit(sess, privkey_tmpl, privkey_tmpl_count); + if (rv != CKR_OK) { + (void) fprintf(stderr, "Cannot intialize private key search." + " error = %s\n", pkcs11_strerror(rv)); + return (NULL); + } + + rv = C_FindObjects(sess, &privkey_obj, 1, &privkey_obj_count); + if (rv != CKR_OK) { + (void) fprintf(stderr, "Cannot retrieve private key object " + " error = %s\n", pkcs11_strerror(rv)); + return (NULL); + } + /* Who cares if this fails! */ + (void) C_FindObjectsFinal(sess); + + + (void) printf("found %ld private keys\n", privkey_obj_count); + + if (privkey_obj_count == 0) { + (void) fprintf(stderr, "no private keys. bye.\n"); + return (NULL); + } + + rv = C_GetAttributeValue(sess, privkey_obj, privkey_attrs, 1); + if (rv != CKR_OK) { + (void) fprintf(stderr, + "Cannot get private key object attributes." + " error = %s\n", pkcs11_strerror(rv)); + return (NULL); + } + + if (verbose) { + (void) printf("private key attributes: \n"); + (void) printf("\tmodulus: size %ld value:", + privkey_attrs[0].ulValueLen); + } + + /* Now wrap the key, then unwrap it */ + + { + CK_BYTE aes_key_val[16] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + static CK_BYTE aes_param[16] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + CK_MECHANISM aes_cbc_pad_mech = {CKM_AES_CBC_PAD, aes_param, 16}; + CK_OBJECT_HANDLE aes_key_obj, sess_privkey_obj; + CK_BYTE *wrapped_privkey; + CK_ULONG wrapped_privkey_len; + + CK_ATTRIBUTE unwrap_tmpl[] = { + {CKA_TOKEN, &false, sizeof (false)}, + {CKA_CLASS, &privkey_class, sizeof (privkey_class)}, + {CKA_KEY_TYPE, &privkey_type, sizeof (privkey_type)}, + {CKA_SENSITIVE, &false, sizeof (false)}, + {CKA_PRIVATE, &false, sizeof (false)} + }; + + rv = SUNW_C_KeyToObject(sess, CKM_AES_CBC_PAD, aes_key_val, 16, + &aes_key_obj); + + if (rv != CKR_OK) { + (void) fprintf(stderr, + "Cannot create wrapping key. error = %s\n", + pkcs11_strerror(rv)); + return (NULL); + } + + /* get the size of the wrapped key */ + rv = C_WrapKey(sess, &aes_cbc_pad_mech, aes_key_obj, privkey_obj, + NULL, &wrapped_privkey_len); + if (rv != CKR_OK) { + (void) fprintf(stderr, "Cannot get key size. error = %s\n", + pkcs11_strerror(rv)); + return (NULL); + } + + wrapped_privkey = malloc(wrapped_privkey_len * sizeof (CK_BYTE)); + if (wrapped_privkey == NULL) { + return (NULL); + } + + /* do the actual key wrapping */ + rv = C_WrapKey(sess, &aes_cbc_pad_mech, aes_key_obj, privkey_obj, + wrapped_privkey, &wrapped_privkey_len); + if (rv != CKR_OK) { + (void) fprintf(stderr, "Cannot wrap private key. error = %s\n", + pkcs11_strerror(rv)); + return (NULL); + } + + (void) C_Logout(sess); + (void) printf("private key successfully wrapped, " + "wrapped blob length: %ld\n", + wrapped_privkey_len); + + rv = C_UnwrapKey(sess, &aes_cbc_pad_mech, aes_key_obj, + wrapped_privkey, wrapped_privkey_len, + unwrap_tmpl, 5, &sess_privkey_obj); + if (rv != CKR_OK) { + (void) fprintf(stderr, "Cannot unwrap private key." + " error = %s\n", pkcs11_strerror(rv)); + return (NULL); + } + (void) printf("session private key successfully unwrapped\n"); + + return (pkcs11_to_kssl(sess, sess_privkey_obj, cert_obj, bufsize)); + } +} + + +static kssl_params_t * +openssl_to_kssl(RSA *rsa, uchar_t *cert_buf, int cert_size, int *paramsize) +{ + int i; + kssl_params_t *kssl_params; + kssl_key_t *key; + char *buf; + uint32_t bufsize; + kssl_object_attribute_t kssl_attrs[MAX_ATTR_CNT]; + kssl_object_attribute_t kssl_tmpl_attrs[MAX_ATTR_CNT] = { + {SUN_CKA_MODULUS, NULL, 0}, + {SUN_CKA_PUBLIC_EXPONENT, NULL, 0}, + {SUN_CKA_PRIVATE_EXPONENT, NULL, 0}, + {SUN_CKA_PRIME_1, NULL, 0}, + {SUN_CKA_PRIME_2, NULL, 0}, + {SUN_CKA_EXPONENT_1, NULL, 0}, + {SUN_CKA_EXPONENT_2, NULL, 0}, + {SUN_CKA_COEFFICIENT, NULL, 0} + }; + BIGNUM *priv_key_bignums[MAX_ATTR_CNT]; + int attr_cnt; + + bufsize = sizeof (kssl_params_t); + bufsize += cert_size + sizeof (uint32_t); + + /* and the key attributes */ + priv_key_bignums[0] = rsa->n; /* MODULUS */ + priv_key_bignums[1] = rsa->e; /* PUBLIC_EXPONENT */ + priv_key_bignums[2] = rsa->d; /* PRIVATE_EXPONENT */ + priv_key_bignums[3] = rsa->p; /* PRIME_1 */ + priv_key_bignums[4] = rsa->q; /* PRIME_2 */ + priv_key_bignums[5] = rsa->dmp1; /* EXPONENT_1 */ + priv_key_bignums[6] = rsa->dmq1; /* EXPONENT_2 */ + priv_key_bignums[7] = rsa->iqmp; /* COEFFICIENT */ + + if (rsa->n == NULL || rsa->d == NULL) { + (void) fprintf(stderr, + "missing required attributes in private key.\n"); + return (NULL); + } + + attr_cnt = 0; + for (i = 0; i < MAX_ATTR_CNT; i++) { + if (priv_key_bignums[i] == NULL) + continue; + kssl_attrs[attr_cnt].ka_type = kssl_tmpl_attrs[i].ka_type; + kssl_attrs[attr_cnt].ka_value_len = + BN_num_bytes(priv_key_bignums[i]); + bufsize += sizeof (crypto_object_attribute_t) + + kssl_attrs[attr_cnt].ka_value_len; + attr_cnt++; + } + + /* Now the big memory allocation */ + if ((buf = calloc(bufsize, 1)) == NULL) { + (void) fprintf(stderr, + "Cannot allocate memory for the kssl_params " + "and values\n"); + return (NULL); + } + + /* LINTED */ + kssl_params = (kssl_params_t *)buf; + + buf = (char *)(kssl_params + 1); + + kssl_params->kssl_certs.sc_count = 1; + bcopy(&cert_size, buf, sizeof (uint32_t)); + kssl_params->kssl_certs.sc_sizes_offset = buf - (char *)kssl_params; + buf += sizeof (uint32_t); + + /* the keys attributes structs array */ + key = &kssl_params->kssl_privkey; + key->ks_format = CRYPTO_KEY_ATTR_LIST; + key->ks_count = attr_cnt; + key->ks_attrs_offset = buf - (char *)kssl_params; + buf += attr_cnt * sizeof (kssl_object_attribute_t); + + /* now the certs values */ + bcopy(cert_buf, buf, cert_size); + kssl_params->kssl_certs.sc_certs_offset = buf - (char *)kssl_params; + buf += cert_size; + + attr_cnt = 0; + /* then the key attributes values */ + for (i = 0; i < MAX_ATTR_CNT; i++) { + if (priv_key_bignums[i] == NULL) + continue; + (void) BN_bn2bin(priv_key_bignums[i], (unsigned char *)buf); + kssl_attrs[attr_cnt].ka_value_offset = + buf - (char *)kssl_params; + buf += kssl_attrs[attr_cnt].ka_value_len; + attr_cnt++; + } + + bcopy(kssl_attrs, ((char *)kssl_params) + key->ks_attrs_offset, + attr_cnt * sizeof (kssl_object_attribute_t)); + + *paramsize = bufsize; + return (kssl_params); +} + +kssl_params_t * +load_from_pem(const char *filename, const char *password_file, int *paramsize) +{ + uchar_t *cert_buf; + int cert_size; + RSA *rsa; + kssl_params_t *kssl_params; + + rsa = PEM_get_rsa_key(filename, (char *)password_file); + if (rsa == NULL) { + (void) fprintf(stderr, "cannot read the private key\n"); + return (NULL); + } + + if (verbose) + (void) printf("private key read successfully\n"); + + cert_buf = PEM_get_cert(filename, (char *)password_file, &cert_size); + if (cert_buf == NULL) { + RSA_free(rsa); + return (NULL); + } + + if (verbose) + (void) printf("certificate read successfully size=%d\n", + cert_size); + + kssl_params = openssl_to_kssl(rsa, cert_buf, cert_size, paramsize); + + free(cert_buf); + RSA_free(rsa); + return (kssl_params); +} + +kssl_params_t * +load_from_pkcs12(const char *filename, const char *password_file, + int *paramsize) +{ + uchar_t *cert_buf; + int cert_size; + RSA *rsa; + kssl_params_t *kssl_params; + + if (PKCS12_get_rsa_key_cert(filename, password_file, &rsa, &cert_buf, + &cert_size) < 0) { + (void) fprintf(stderr, + "Unable to read cert and/or key from %s\n", filename); + return (NULL); + } + + if (verbose) + (void) printf( + "key/certificate read successfully cert_size=%d\n", + cert_size); + + kssl_params = openssl_to_kssl(rsa, cert_buf, cert_size, paramsize); + + free(cert_buf); + RSA_free(rsa); + return (kssl_params); +} + + +int +parse_and_set_addr(char *server_address, char *server_port, + struct sockaddr_in *addr) +{ + if (server_port == NULL) { + return (-1); + } + + if (server_address == NULL) { + addr->sin_addr.s_addr = INADDR_ANY; + } else { + addr->sin_addr.s_addr = inet_addr(server_address); + if ((int)addr->sin_addr.s_addr == -1) { + struct hostent *hp; + + if ((hp = gethostbyname(server_address)) == NULL) { + (void) fprintf(stderr, + "Error: Unknown host: %s\n", + server_address); + return (-1); + } + + (void) memcpy(&addr->sin_addr.s_addr, + hp->h_addr_list[0], + sizeof (addr->sin_addr.s_addr)); + } + } + + errno = 0; + addr->sin_port = strtol(server_port, NULL, 10); + if (addr->sin_port == 0 || errno != 0) { + (void) fprintf(stderr, "Error: Invalid Port value: %s\n", + server_port); + return (-1); + } + + return (0); +} + +/* + * The order of the ciphers is important. It is used as the + * default order (when -c is not specified). + */ +struct csuite { + const char *suite; + uint16_t val; + boolean_t seen; +} cipher_suites[CIPHER_SUITE_COUNT - 1] = { + {"rsa_rc4_128_sha", SSL_RSA_WITH_RC4_128_SHA, B_FALSE}, + {"rsa_rc4_128_md5", SSL_RSA_WITH_RC4_128_MD5, B_FALSE}, + {"rsa_3des_ede_cbc_sha", SSL_RSA_WITH_3DES_EDE_CBC_SHA, B_FALSE}, + {"rsa_des_cbc_sha", SSL_RSA_WITH_DES_CBC_SHA, B_FALSE}, +}; + +int +check_suites(char *suites, uint16_t *sarray) +{ + int i; + int err = 0; + char *suite; + int sindx = 0; + + if (suites != NULL) { + for (i = 0; i < CIPHER_SUITE_COUNT - 1; i++) + sarray[i] = CIPHER_NOTSET; + } else { + for (i = 0; i < CIPHER_SUITE_COUNT - 1; i++) + sarray[i] = cipher_suites[i].val; + return (err); + } + + suite = strtok(suites, ","); + do { + for (i = 0; i < CIPHER_SUITE_COUNT - 1; i++) { + if (strcasecmp(suite, cipher_suites[i].suite) == 0) { + if (!cipher_suites[i].seen) { + sarray[sindx++] = cipher_suites[i].val; + cipher_suites[i].seen = B_TRUE; + } + break; + } + } + + if (i == (CIPHER_SUITE_COUNT - 1)) { + (void) fprintf(stderr, + "Unknown Cipher suite name: %s\n", suite); + err++; + } + } while ((suite = strtok(NULL, ",")) != NULL); + + return (err); +} + +int +do_create(int argc, char *argv[]) +{ + const char *softtoken_dir = NULL; + const char *token_label = NULL; + const char *password_file = NULL; + const char *filename = NULL; + const char *certname = NULL; + char *suites = NULL; + uint32_t timeout = DEFAULT_SID_TIMEOUT; + uint32_t scache_size = DEFAULT_SID_CACHE_NENTRIES; + int proxy_port = -1; + struct sockaddr_in server_addr; + char *format = NULL; + char *port, *addr; + char c; + int pcnt; + kssl_params_t *kssl_params; + int bufsize; + + argc -= 1; + argv += 1; + + while ((c = getopt(argc, argv, "vT:d:f:i:p:c:C:t:x:z:")) != -1) { + switch (c) { + case 'd': + softtoken_dir = optarg; + break; + case 'c': + suites = optarg; + break; + case 'C': + certname = optarg; + break; + case 'f': + format = optarg; + break; + case 'i': + filename = optarg; + break; + case 'T': + token_label = optarg; + break; + case 'p': + password_file = optarg; + break; + case 't': + timeout = atoi(optarg); + break; + case 'x': + proxy_port = atoi(optarg); + break; + case 'v': + verbose = B_TRUE; + break; + case 'z': + scache_size = atoi(optarg); + break; + default: + goto err; + } + } + + pcnt = argc - optind; + if (pcnt == 0) { + port = "443"; /* default SSL port */ + addr = NULL; + } else if (pcnt == 1) { + port = argv[optind]; + addr = NULL; + } else if (pcnt == 2) { + addr = argv[optind]; + port = argv[optind + 1]; + } else { + goto err; + } + + if (parse_and_set_addr(addr, port, &server_addr) < 0) { + goto err; + } + + if (verbose) { + (void) printf("addr=%s, port = %d\n", + inet_ntoa(server_addr.sin_addr), server_addr.sin_port); + } + + if (format == NULL || proxy_port == -1) { + goto err; + } + + if (strcmp(format, "pkcs11") == 0) { + if (token_label == NULL || certname == NULL) { + goto err; + } + if (softtoken_dir != NULL) { + (void) setenv("SOFTTOKEN_DIR", softtoken_dir, 1); + if (verbose) { + (void) printf( + "SOFTTOKEN_DIR=%s\n", + getenv("SOFTTOKEN_DIR")); + } + } + kssl_params = load_from_pkcs11( + token_label, password_file, certname, &bufsize); + } else if (strcmp(format, "pkcs12") == 0) { + if (filename == NULL) { + goto err; + } + kssl_params = load_from_pkcs12( + filename, password_file, &bufsize); + } else if (strcmp(format, "pem") == 0) { + if (filename == NULL) { + goto err; + } + kssl_params = load_from_pem( + filename, password_file, &bufsize); + } else { + (void) fprintf(stderr, "Unsupported cert format: %s\n", format); + goto err; + } + + if (kssl_params == NULL) { + return (FAILURE); + } + + if (check_suites(suites, kssl_params->kssl_suites) != 0) + goto err; + + kssl_params->kssl_params_size = bufsize; + kssl_params->kssl_addr = server_addr; + kssl_params->kssl_session_cache_timeout = timeout; + kssl_params->kssl_proxy_port = proxy_port; + kssl_params->kssl_session_cache_size = scache_size; + + if (kssl_send_command((char *)kssl_params, KSSL_ADD_ENTRY) < 0) { + (void) fprintf(stderr, "Error loading cert and key"); + return (FAILURE); + } + + if (verbose) + (void) printf("Successfully loaded cert and key\n"); + + free(kssl_params); + return (SUCCESS); + +err: + usage_create(B_TRUE); + return (SMF_EXIT_ERR_CONFIG); +} diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/cmd/cmd-inet/usr.sbin/kssl/kssladm/kssladm_delete.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/cmd-inet/usr.sbin/kssl/kssladm/kssladm_delete.c Sat Nov 12 18:58:05 2005 -0800 @@ -0,0 +1,98 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include /* struct sockaddr_in */ +#include +#include +#include +#include +#include +#include +#include "kssladm.h" + +void +usage_delete(boolean_t do_print) +{ + if (do_print) + (void) fprintf(stderr, "Usage:\n"); + (void) fprintf(stderr, + "kssladm delete [-v] [] \n"); +} + +int +do_delete(int argc, char *argv[]) +{ + struct sockaddr_in server_addr; + char c; + char *port, *addr; + int pcnt; + + if (argc < 3) { + goto err; + } + + argc -= 1; + argv += 1; + + while ((c = getopt(argc, argv, "v")) != -1) { + switch (c) { + case 'v': + verbose = B_TRUE; + break; + default: + goto err; + } + } + + pcnt = argc - optind; + if (pcnt == 1) { + port = argv[optind]; + addr = NULL; + } else if (pcnt == 2) { + addr = argv[optind]; + port = argv[optind + 1]; + } + + if (parse_and_set_addr(addr, port, &server_addr) < 0) { + goto err; + } + + if (kssl_send_command((char *)&server_addr, KSSL_DELETE_ENTRY) < 0) { + perror("Error deleting entry"); + return (FAILURE); + } + + if (verbose) + (void) printf("Successfully loaded cert and key\n"); + + return (SUCCESS); + +err: + usage_delete(B_TRUE); + return (SMF_EXIT_ERR_CONFIG); +} diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/cmd/cmd-inet/usr.sbin/kssl/kssladm/openssl_util.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/cmd-inet/usr.sbin/kssl/kssladm/openssl_util.c Sat Nov 12 18:58:05 2005 -0800 @@ -0,0 +1,265 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include "kssladm.h" + +static void +print_crypto_error(void) +{ + ERR_load_crypto_strings(); + ERR_print_errors_fp(stderr); +} + +/* ARGSUSED */ +static int +pem_password_callback(char *buf, int size, int rwflag, void *userdata) +{ + return (get_passphrase((const char *)userdata, buf, size)); +} + + +static STACK_OF(X509_INFO) * +PEM_get_x509_info_stack(const char *filename, char *passphrase) +{ + FILE *fp; + STACK_OF(X509_INFO) *x509_info_stack; + + fp = fopen(filename, "r"); + if (fp == NULL) { + perror("Unable to open pem file for reading"); + return (NULL); + } + if (verbose) + (void) printf("In PEM_get_x509_info_stack: %s opened\n", + filename); + + OpenSSL_add_all_algorithms(); + + x509_info_stack = PEM_X509_INFO_read( + fp, NULL, pem_password_callback, passphrase); + (void) fclose(fp); + + if (x509_info_stack == NULL) { + print_crypto_error(); + } + + return (x509_info_stack); +} + + +RSA * +PEM_get_rsa_key(const char *filename, char *passphrase) +{ + FILE *fp; + RSA *rsa_key; + + fp = fopen(filename, "r"); + if (fp == NULL) { + perror("Unable to open pem file for reading"); + return (NULL); + } + if (verbose) + (void) printf("In PEM_get_rsa_key: %s opened\n", filename); + + OpenSSL_add_all_algorithms(); + + rsa_key = PEM_read_RSAPrivateKey( + fp, NULL, pem_password_callback, passphrase); + (void) fclose(fp); + + if (rsa_key == NULL) { + print_crypto_error(); + } + + return (rsa_key); +} + +uchar_t * +get_modulus(uchar_t *ber_buf, int buflen, int *modlen) +{ + int i, j, v; + X509 *x; + EVP_PKEY *pkey; + BIGNUM *bn; + uchar_t *m = NULL, *mptr; + + x = d2i_X509(NULL, &ber_buf, buflen); + if (x != NULL) { + pkey = X509_get_pubkey(x); + if (pkey == NULL) { + X509_free(x); + return (NULL); + } + + bn = pkey->pkey.rsa->n; + mptr = m = malloc(bn->top * BN_BYTES); + for (i = bn->top - 1; i >= 0; i--) { + for (j = BN_BITS2 - 8; j >= 0; j -= 8) { + v = ((int)(bn->d[i] >> (long)j)) & 0xff; + *m = v; + m++; + } + } + *modlen = bn->top * BN_BYTES; + EVP_PKEY_free(pkey); + X509_free(x); + } + + return (mptr); +} + +static uchar_t * +X509_to_bytes(X509 *cert, int *cert_size) +{ + uchar_t *cert_buf = NULL; + int size; + + size = i2d_X509(cert, &cert_buf); + if (size < 0) { + perror("Invalid cert\n"); + return (NULL); + } + + *cert_size = size; + return (cert_buf); +} + + +/* Returns DER encoded cert */ +uchar_t * +PEM_get_cert(const char *filename, char *passphrase, int *cert_size) +{ + STACK_OF(X509_INFO) *x509_info_stack; + uchar_t *cert_buf; + X509_INFO *info; + + x509_info_stack = PEM_get_x509_info_stack(filename, passphrase); + if (x509_info_stack == NULL) { + return (NULL); + } + + /* LINTED */ + info = sk_X509_INFO_pop(x509_info_stack); + if (info == NULL || info->x509 == NULL) { + (void) fprintf(stderr, "No cert found\n"); + return (NULL); + } + + cert_buf = X509_to_bytes(info->x509, cert_size); + X509_INFO_free(info); + return (cert_buf); +} + +#include +static PKCS12 * +PKCS12_load(const char *filename) +{ + FILE *fp; + PKCS12 *p12; + + fp = fopen(filename, "r"); + if (fp == NULL) { + perror("Unnable to open file for reading"); + return (NULL); + } + + OpenSSL_add_all_algorithms(); + + p12 = d2i_PKCS12_fp(fp, NULL); + (void) fclose(fp); + if (p12 == NULL) { + ERR_load_PKCS12_strings(); + ERR_print_errors_fp(stderr); + (void) fprintf(stderr, "Unable to load from %s\n", filename); + return (NULL); + } + + return (p12); +} + +int +PKCS12_get_rsa_key_cert(const char *filename, const char *password_file, + RSA **rsa, uchar_t **cert, int *cert_size) +{ + int rv = -1; + EVP_PKEY *pkey = NULL; + X509 *x509 = NULL; + char *password = NULL; + char password_buf[1024]; + PKCS12 *p12; + + p12 = PKCS12_load(filename); + if (p12 == NULL) { + goto out0; + } + + if (! PKCS12_verify_mac(p12, NULL, 0)) { + if (get_passphrase( + password_file, password_buf, sizeof (password_buf)) <= 0) { + perror("Unnable to read passphrase"); + goto out0; + } + + password = password_buf; + } + + (void) PKCS12_parse(p12, password, &pkey, &x509, NULL); + + PKCS12_free(p12); + if (pkey == NULL) { + (void) fprintf(stderr, "No key returned\n"); + goto out0; + } + if (x509 == NULL) { + (void) fprintf(stderr, "No cert returned\n"); + goto out1; + } + + *rsa = EVP_PKEY_get1_RSA(pkey); + if (*rsa == NULL) { + goto out2; + } + + *cert = X509_to_bytes(x509, cert_size); + + if (*cert != NULL) { + rv = 0; + } + +out2: + X509_free(x509); +out1: + EVP_PKEY_free(pkey); +out0: + return (rv); +} diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/cmd/cmd-inet/usr.sbin/kssl/ksslcfg/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/cmd-inet/usr.sbin/kssl/ksslcfg/Makefile Sat Nov 12 18:58:05 2005 -0800 @@ -0,0 +1,87 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# cmd/cmd-inet/usr.sbin/kssl/kssladm/Makefile +# + +PROG= ksslcfg +MANIFEST= kssl-proxy.xml + +include $(SRC)/cmd/Makefile.cmd + +OBJS = \ + ksslcfg.o \ + ksslcfg_create.o \ + ksslcfg_delete.o + +POFILES = $(OBJS:%.o=%.po) +POFILE = $(PROG)_all.po + +SRCS = $(OBJS:%.o=%.c) + +ROOTMANIFESTDIR= $(ROOTSVCNETWORK)/ssl +$(ROOTMANIFEST):= FILEMODE= 444 + +$(ROOTMANIFESTDIR)/%: % + $(INS.file) + +.KEEP_STATE: + +CFLAGS += $(CCVERBOSE) + +LDLIBS += -lscf -lnsl + +all: $(PROG) + +$(PROG): $(OBJS) + $(LINK.c) $(OBJS) -o $@ $(LDLIBS) $(DYNFLAGS) + $(POST_PROCESS) + +$(POFILE): $(POFILES) + $(RM) $@; cat $(POFILES) > $@ + +install: all $(ROOTUSRSBINPROG) $(ROOTMANIFEST) + +clean: + $(RM) $(OBJS) + +check: $(CHKMANIFEST) + $(CSTYLE) -pP $(SRCS) + +lint: lint_SRCS + +$(ROOTUSRSBINPROG): $(ROOTUSRSBIN) + +$(ROOTUSRSBIN): + $(MKDIR) -p $@ + +$(ROOTMANIFEST): $(ROOTSVCNETWORK)/ssl + +$(ROOTSVCNETWORK)/ssl: + $(MKDIR) -p $@ + +include ../../../../Makefile.targ diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/cmd/cmd-inet/usr.sbin/kssl/ksslcfg/kssl-proxy.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/cmd-inet/usr.sbin/kssl/ksslcfg/kssl-proxy.xml Sat Nov 12 18:58:05 2005 -0800 @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/cmd/cmd-inet/usr.sbin/kssl/ksslcfg/ksslcfg.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/cmd-inet/usr.sbin/kssl/ksslcfg/ksslcfg.c Sat Nov 12 18:58:05 2005 -0800 @@ -0,0 +1,246 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include /* inet_addr() */ +#include +#include +#include /* hostent */ +#include /* ip_addr_t */ +#include +#include +#include +#include +#include +#include +#include +#include "ksslcfg.h" + +/* + * ksslcfg(1M) + * + * ksslcfg manages smf(5) instances for the Kernel SSL proxy module. + * It makes use of kssladm(1M) which does the grunt work. + */ + +#define KSSLCFG_VERSION "1.0" + +boolean_t verbose = B_FALSE; +const char *SERVICE_NAME = "network/ssl/proxy"; + +void +KSSL_DEBUG(const char *format, ...) +{ + va_list ap; + + if (verbose) { + va_start(ap, format); + (void) vprintf(format, ap); + va_end(ap); + } +} + +int +get_portnum(const char *s, ushort_t *rport) +{ + unsigned long port; + + errno = 0; + port = strtoul(s, NULL, 10); + if (port > USHRT_MAX || port == 0 || errno != 0) { + return (0); + } + + if (rport != NULL) + *rport = (ushort_t)port; + return (1); +} + +#define ANY_ADDR "INADDR_ANY" + +/* + * An instance name is formed using either the host name in the fully + * qualified domain name form (FQDN) which should map to a specific IP address + * or using INADDR_ANY which means all IP addresses. + * + * We do a lookup or reverse lookup to get the host name. It is assumed that + * the returned name is in the FQDN form. i.e. DNS is used. + */ +char * +create_instance_name(const char *arg, char **inaddr_any_name, + boolean_t is_create) +{ + int len; + uint16_t port; + char *cname; + in_addr_t addr; + char *instance_name; + const char *prefix = "kssl-"; + char *first_space = strchr(arg, ' '); + + if (first_space == NULL) { + if (get_portnum(arg, &port) == 0) { + (void) fprintf(stderr, + gettext("Error: Invalid port value -- %s\n"), + arg); + return (NULL); + } + KSSL_DEBUG("port=%d\n", port); + if ((cname = strdup(ANY_ADDR)) == NULL) + return (NULL); + } else { + char *temp_str; + char *ptr; + struct hostent *hp; + boolean_t do_warn; + + if (get_portnum(first_space + 1, &port) == 0) { + (void) fprintf(stderr, + gettext("Error: Invalid port value -- %s\n"), + first_space + 1); + return (NULL); + } + KSSL_DEBUG("port=%d\n", port); + + if ((temp_str = strdup(arg)) == NULL) + return (NULL); + *(strchr(temp_str, ' ')) = '\0'; + + if ((int)(addr = inet_addr(temp_str)) == -1) { + if ((hp = gethostbyname(temp_str)) == NULL) { + (void) fprintf(stderr, + gettext("Error: Unknown host -- %s\n"), + temp_str); + free(temp_str); + return (NULL); + } + } else { + /* This is an IP address. Do a reverse lookup. */ + if ((hp = gethostbyaddr((char *)&addr, 4, AF_INET)) + == NULL) { + (void) fprintf(stderr, + gettext("Error: Unknown host -- %s\n"), + temp_str); + free(temp_str); + return (NULL); + } + } + + if ((ptr = cname = strdup(hp->h_name)) == NULL) { + free(temp_str); + return (NULL); + } + do_warn = B_TRUE; + /* "s/./-/g" */ + while ((ptr = strchr(ptr, '.')) != NULL) { + if (do_warn) + do_warn = B_FALSE; + *ptr = '-'; + ptr++; + } + + if (do_warn && is_create) { + (void) fprintf(stderr, + gettext("Warning: %s does not appear to have a" + " registered DNS name.\n"), temp_str); + } + + free(temp_str); + } + + KSSL_DEBUG("Cannonical host name =%s\n", cname); + + len = strlen(prefix) + strlen(cname) + 10; + if ((instance_name = malloc(len)) == NULL) { + (void) fprintf(stderr, + gettext("Error: memory allocation failure.\n")); + return (NULL); + } + (void) snprintf(instance_name, len, "%s%s-%d", prefix, cname, port); + + if (is_create) { + len = strlen(prefix) + strlen(ANY_ADDR) + 10; + if ((*inaddr_any_name = malloc(len)) == NULL) { + (void) fprintf(stderr, + gettext("Error: memory allocation failure.\n")); + free(cname); + return (NULL); + } + + (void) snprintf(*inaddr_any_name, len, + "%s%s-%d", prefix, ANY_ADDR, port); + } + + free(cname); + KSSL_DEBUG("instance_name=%s\n", instance_name); + return (instance_name); +} + +static void +usage_all(void) +{ + (void) fprintf(stderr, gettext("Usage:\n")); + usage_create(B_FALSE); + usage_delete(B_FALSE); + (void) fprintf(stderr, "ksslcfg -V\n"); + (void) fprintf(stderr, "ksslcfg -?\n"); +} + + +int +main(int argc, char **argv) +{ + int rv = SUCCESS; + + (void) setlocale(LC_ALL, ""); +#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ +#define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ +#endif + (void) textdomain(TEXT_DOMAIN); + + if (argc < 2) { + usage_all(); + return (ERROR_USAGE); + } + + if (strcmp(argv[1], "create") == 0) { + rv = do_create(argc, argv); + } else if (strcmp(argv[1], "delete") == 0) { + rv = do_delete(argc, argv); + } else if (strcmp(argv[1], "-V") == 0) { + (void) printf("%s\n", KSSLCFG_VERSION); + } else if (strcmp(argv[1], "-?") == 0) { + usage_all(); + } else { + (void) fprintf(stderr, + gettext("Error: Unknown subcommand -- %s\n"), argv[1]); + usage_all(); + rv = ERROR_USAGE; + } + + return (rv); +} diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/cmd/cmd-inet/usr.sbin/kssl/ksslcfg/ksslcfg.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/cmd-inet/usr.sbin/kssl/ksslcfg/ksslcfg.h Sat Nov 12 18:58:05 2005 -0800 @@ -0,0 +1,69 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _KSSLCFG_H +#define _KSSLCFG_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Common routines and variables used by ksslcfg files. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#define MAX_ADRPORT_LEN 128 /* sufficient for host name/IP address + port */ + +#define SUCCESS 0 +#define FAILURE 1 +#define ERROR_USAGE 2 +#define INSTANCE_ANY_EXISTS 3 +#define INSTANCE_OTHER_EXISTS 4 + +extern const char *SERVICE_NAME; +extern boolean_t verbose; + +extern char *create_instance_name(const char *arg, char **inaddr_any_name, + boolean_t is_create); +int get_portnum(const char *, ushort_t *); +extern void KSSL_DEBUG(const char *format, ...); +extern int do_create(int argc, char *argv[]); +extern int do_delete(int argc, char *argv[]); +extern int delete_instance(const char *instance_name); +extern void usage_create(boolean_t do_print); +extern void usage_delete(boolean_t do_print); + +#ifdef __cplusplus +} +#endif + +#endif /* _KSSLCFG_H */ diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/cmd/cmd-inet/usr.sbin/kssl/ksslcfg/ksslcfg_create.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/cmd-inet/usr.sbin/kssl/ksslcfg/ksslcfg_create.c Sat Nov 12 18:58:05 2005 -0800 @@ -0,0 +1,665 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + + +#include +#include +#include +#include +#include +#include +#include "ksslcfg.h" + +void +usage_create(boolean_t do_print) +{ + if (do_print) + (void) fprintf(stderr, gettext("Usage:\n")); + (void) fprintf(stderr, "ksslcfg create" + " -f pkcs11 [-d softtoken_directory] -T " + " -C -x " + " [options] [] \n"); + + (void) fprintf(stderr, "ksslcfg create" + " -f pkcs12 -i -x " + " [options] [] \n"); + + (void) fprintf(stderr, "ksslcfg create" + " -f pem -i -x " + " [options] [] \n"); + + (void) fprintf(stderr, gettext("options are:\n")); + (void) fprintf(stderr, "\t[-c ]\n" + "\t[-p ]\n" + "\t[-t ]\n" + "\t[-u ]\n" + "\t[-z ]\n" + "\t[-v]\n"); +} + +static scf_propertygroup_t * +add_property_group_to_instance(scf_handle_t *handle, scf_instance_t *instance, + const char *pg_name, const char *pg_type) +{ + scf_propertygroup_t *pg; + + pg = scf_pg_create(handle); + if (pg == NULL) { + KSSL_DEBUG("scf_pg_create failed: %s\n", + scf_strerror(scf_error())); + (void) fprintf(stderr, gettext( + "Unexpected fatal libscf error: %s. Exiting.\n"), + scf_strerror(scf_error())); + return (NULL); + } + + if (scf_instance_add_pg(instance, pg_name, pg_type, 0, pg) != 0) { + KSSL_DEBUG("ERROR: scf_instance_add_pg failed: %s\n", + scf_strerror(scf_error())); + if (scf_error() == SCF_ERROR_EXISTS) + (void) fprintf(stderr, gettext( + "Error: another process is modifying this instance." + " Exiting.\n")); + else + (void) fprintf(stderr, gettext( + "Unexpected fatal libscf error: %s. Exiting.\n"), + scf_strerror(scf_error())); + scf_pg_destroy(pg); + return (NULL); + } else { + KSSL_DEBUG("property group created\n"); + } + + return (pg); +} + +static int +add_new_property(scf_handle_t *handle, const char *prop_name, + scf_type_t type, const char *val, scf_transaction_t *tx) +{ + scf_value_t *value = NULL; + scf_transaction_entry_t *entry = NULL; + int status = FAILURE; + + entry = scf_entry_create(handle); + if (entry == NULL) { + KSSL_DEBUG("scf_entry_create failed: %s\n", + scf_strerror(scf_error())); + goto out; + } + KSSL_DEBUG("scf_entry_create succeeded\n"); + + value = scf_value_create(handle); + if (value == NULL) { + goto out; + } + KSSL_DEBUG("scf_value_create succeeded\n"); + + if (scf_transaction_property_new(tx, entry, prop_name, type) != 0) { + goto out; + } + KSSL_DEBUG("scf_transaction_property_new succeeded\n"); + + if (scf_value_set_from_string(value, type, val) != 0) { + goto out; + } + KSSL_DEBUG("scf_value_set_from_string \'%s\' succeeded\n", val); + + if (scf_entry_add_value(entry, value) != 0) { + KSSL_DEBUG( + "scf_entry_add_value failed: %s\n", + scf_strerror(scf_error())); + goto out; + } + KSSL_DEBUG("scf_entry_add_value succeeded\n"); + + status = SUCCESS; + +out: + if (status != SUCCESS) + (void) fprintf(stderr, gettext( + "Unexpected fatal libscf error: %s. Exiting.\n"), + scf_strerror(scf_error())); + return (status); +} + +static int +set_method_context(scf_handle_t *handle, scf_transaction_t *tran, + const char *value_str) +{ + if ((add_new_property(handle, SCF_PROPERTY_USE_PROFILE, + SCF_TYPE_BOOLEAN, "false", tran) != SUCCESS) || + (add_new_property(handle, SCF_PROPERTY_USER, SCF_TYPE_ASTRING, + value_str, tran) != SUCCESS) || + (add_new_property(handle, SCF_PROPERTY_GROUP, SCF_TYPE_ASTRING, + ":default", tran) != SUCCESS) || + (add_new_property(handle, SCF_PROPERTY_LIMIT_PRIVILEGES, + SCF_TYPE_ASTRING, ":default", tran) != SUCCESS) || + (add_new_property(handle, SCF_PROPERTY_WORKING_DIRECTORY, + SCF_TYPE_ASTRING, ":default", tran) != SUCCESS) || + (add_new_property(handle, SCF_PROPERTY_SUPP_GROUPS, + SCF_TYPE_ASTRING, ":default", tran) != SUCCESS) || + (add_new_property(handle, SCF_PROPERTY_RESOURCE_POOL, + SCF_TYPE_ASTRING, ":default", tran) != SUCCESS) || + (add_new_property(handle, SCF_PROPERTY_PROJECT, SCF_TYPE_ASTRING, + ":default", tran) != SUCCESS) || + (add_new_property(handle, SCF_PROPERTY_PRIVILEGES, + SCF_TYPE_ASTRING, "basic,sys_net_config", tran) != SUCCESS)) + return (FAILURE); + + return (SUCCESS); +} + +static int +add_pg_method(scf_handle_t *handle, scf_instance_t *instance, + const char *kssl_entry, const char *pg_name, const char *flags, + const char *value_str) +{ + int len, rv; + char *command; + const char *base_command; + int status = FAILURE; + boolean_t errflag = B_FALSE; + scf_transaction_t *tran; + scf_propertygroup_t *pg; + + pg = add_property_group_to_instance(handle, instance, + pg_name, SCF_GROUP_METHOD); + if (pg == NULL) { + /* flag is false to suppress duplicate error messages */ + errflag = B_FALSE; + goto out0; + } + KSSL_DEBUG("%s method added\n", pg_name); + + tran = scf_transaction_create(handle); + if (tran == NULL) { + KSSL_DEBUG("scf_transaction_create failed: %s\n", + scf_strerror(scf_error())); + errflag = B_TRUE; + goto out0; + } + KSSL_DEBUG("scf_transaction_create succeeded\n"); + + do { + if (scf_transaction_start(tran, pg) != 0) { + KSSL_DEBUG("scf_transaction_start failed: %s\n", + scf_strerror(scf_error())); + if (scf_error() == SCF_ERROR_PERMISSION_DENIED) { + (void) fprintf(stderr, gettext( + "Error: Permission denied.\n")); + errflag = B_FALSE; + } else if (scf_error() == SCF_ERROR_DELETED) { + (void) fprintf(stderr, gettext( + "Error: property group %s has" + " been deleted.\n"), pg_name); + errflag = B_FALSE; + } else + errflag = B_TRUE; + goto out1; + } + KSSL_DEBUG("scf_transaction_start succeeded\n"); + + if (strcmp(pg_name, "stop") == 0) + base_command = "/usr/lib/kssladm delete"; + else + base_command = "/usr/lib/kssladm create"; + + len = strlen(base_command) + strlen(flags) + + strlen(kssl_entry) + 3; + + command = malloc(len); + if (command == NULL) { + goto out2; + } + + (void) snprintf(command, len, "%s %s %s", + base_command, flags, kssl_entry); + KSSL_DEBUG("command=%s\n", command); + + if (add_new_property(handle, SCF_PROPERTY_EXEC, + SCF_TYPE_ASTRING, command, tran) != SUCCESS) { + free(command); + goto out2; + } + free(command); + + if (add_new_property(handle, SCF_PROPERTY_TIMEOUT, + SCF_TYPE_COUNT, "60", tran) != SUCCESS) + goto out2; + + if (set_method_context(handle, tran, value_str) != SUCCESS) + goto out2; + + rv = scf_transaction_commit(tran); + switch (rv) { + case 1: + KSSL_DEBUG("scf_transaction_commit succeeded\n"); + status = SUCCESS; + goto out2; + case 0: + scf_transaction_reset(tran); + if (scf_pg_update(pg) == -1) { + goto out2; + } + break; + case -1: + default: + KSSL_DEBUG("ERROR: scf_transaction_commit failed: %s\n", + scf_strerror(scf_error())); + if (scf_error() == SCF_ERROR_PERMISSION_DENIED) { + (void) fprintf(stderr, gettext( + "Error: Permission denied.\n")); + errflag = B_FALSE; + } else { + errflag = B_TRUE; + } + goto out2; + } + } while (rv == 0); + +out2: + scf_transaction_reset(tran); +out1: + scf_transaction_destroy_children(tran); + scf_transaction_destroy(tran); +out0: + if (pg != NULL) + scf_pg_destroy(pg); + if (errflag) + (void) fprintf(stderr, gettext( + "Unexpected fatal libscf error: %s. Exiting.\n"), + scf_strerror(scf_error())); + return (status); +} + +static int +create_instance(scf_handle_t *handle, scf_service_t *svc, + const char *instance_name, const char *kssl_entry, const char *command, + const char *username, char *inaddr_any_name) +{ + int status = FAILURE; + char *buf; + boolean_t errflag = B_FALSE; + ssize_t max_fmri_len; + scf_instance_t *instance; + + instance = scf_instance_create(handle); + if (instance == NULL) { + errflag = B_TRUE; + KSSL_DEBUG("scf_instance_create failed: %s\n", + scf_strerror(scf_error())); + goto out; + } + KSSL_DEBUG("scf_instance_create succeeded\n"); + + if (scf_service_get_instance(svc, inaddr_any_name, instance) == 0) { + /* Let the caller deal with the duplicate instance */ + status = INSTANCE_ANY_EXISTS; + goto out; + } + + if (scf_service_add_instance(svc, instance_name, instance) != 0) { + if (scf_error() == SCF_ERROR_EXISTS) { + /* Let the caller deal with the duplicate instance */ + status = INSTANCE_OTHER_EXISTS; + goto out; + } + + errflag = B_TRUE; + KSSL_DEBUG("scf_service_add_instance failed: %s\n", + scf_strerror(scf_error())); + goto out; + } + KSSL_DEBUG("scf_service_add_instance succeeded\n"); + + if ((add_pg_method(handle, instance, kssl_entry, "start", + command, username) != SUCCESS) || + (add_pg_method(handle, instance, kssl_entry, "refresh", + command, username) != SUCCESS) || + (add_pg_method(handle, instance, kssl_entry, "stop", + "", username) != SUCCESS)) { + scf_instance_destroy(instance); + return (status); + } + + /* enabling the instance */ + max_fmri_len = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH); + if ((buf = malloc(max_fmri_len + 1)) == NULL) + goto out; + + if (scf_instance_to_fmri(instance, buf, max_fmri_len + 1) > 0) { + KSSL_DEBUG("instance_fmri=%s\n", buf); + if (smf_enable_instance(buf, 0) != 0) { + errflag = B_TRUE; + KSSL_DEBUG( + "smf_enable_instance failed: %s\n", + scf_strerror(scf_error())); + goto out; + } + status = SUCCESS; + } + +out: + if (instance != NULL) + scf_instance_destroy(instance); + if (errflag) + (void) fprintf(stderr, gettext( + "Unexpected fatal libscf error: %s. Exiting.\n"), + scf_strerror(scf_error())); + return (status); +} + +static int +create_service(const char *instance_name, const char *kssl_entry, + const char *command, const char *username, char *inaddr_any_name) +{ + int status = FAILURE; + scf_scope_t *scope; + scf_service_t *svc; + scf_handle_t *handle; + boolean_t errflag = B_TRUE; + + handle = scf_handle_create(SCF_VERSION); + if (handle == NULL) { + KSSL_DEBUG("scf_handle_create failed: %s\n", + scf_strerror(scf_error())); + goto out1; + } + KSSL_DEBUG("scf_handle_create succeeded\n"); + + if (scf_handle_bind(handle) == -1) { + KSSL_DEBUG("scf_handle_bind failed: %s\n", + scf_strerror(scf_error())); + goto out1; + } + KSSL_DEBUG("scf_handle_bind succeeded\n"); + + if ((scope = scf_scope_create(handle)) == NULL) { + KSSL_DEBUG("scf_scope_create failed: %s\n", + scf_strerror(scf_error())); + goto out2; + } + KSSL_DEBUG("scf_scope_create succeeded\n"); + + if ((svc = scf_service_create(handle)) == NULL) { + KSSL_DEBUG("scf_service_create failed: %s\n", + scf_strerror(scf_error())); + goto out3; + } + KSSL_DEBUG("scf_service_create succeeded\n"); + + if (scf_handle_decode_fmri(handle, SERVICE_NAME, NULL, svc, + NULL, NULL, NULL, SCF_DECODE_FMRI_EXACT) != 0) { + KSSL_DEBUG("scf_handle_decode_fmri failed: %s\n", + scf_strerror(scf_error())); + if (scf_error() == SCF_ERROR_NOT_FOUND) { + (void) fprintf(stderr, gettext( + "service %s not found in the repository." + " Exiting.\n"), SERVICE_NAME); + errflag = B_FALSE; + } + goto out4; + } + + status = create_instance(handle, svc, instance_name, kssl_entry, + command, username, inaddr_any_name); + +out4: + scf_service_destroy(svc); +out3: + scf_scope_destroy(scope); +out2: + (void) scf_handle_unbind(handle); +out1: + if (handle != NULL) + scf_handle_destroy(handle); + + if (status != SUCCESS && status != INSTANCE_OTHER_EXISTS && + status != INSTANCE_ANY_EXISTS && errflag) + (void) fprintf(stderr, gettext( + "Unexpected fatal libscf error: %s. Exiting.\n"), + scf_strerror(scf_error())); + return (status); +} + +int +do_create(int argc, char *argv[]) +{ + char c; + char *buf, *ptr, *instance_name; + char *inaddr_any_name = NULL; + int i, status, len, pcnt; + const char *token_label = NULL; + const char *filename = NULL; + const char *certname = NULL; + const char *username = NULL; + const char *proxy_port = NULL; + char *format = NULL; + boolean_t quote_next; + char address_port[MAX_ADRPORT_LEN + 1]; + + argc -= 1; + argv += 1; + + /* + * Many of these arguments are passed on to kssladm command + * in the start method of the SMF instance created. So, we do only + * the basic usage checks here and let kssladm check the validity + * of the arguments. This is the reason we ignore optarg + * for some of the cases below. + */ + while ((c = getopt(argc, argv, "vT:d:f:i:p:c:C:t:u:x:z:")) != -1) { + switch (c) { + case 'd': + break; + case 'c': + break; + case 'C': + certname = optarg; + break; + case 'f': + format = optarg; + break; + case 'i': + filename = optarg; + break; + case 'T': + token_label = optarg; + break; + case 'p': + break; + case 't': + break; + case 'u': + username = optarg; + break; + case 'x': + proxy_port = optarg; + break; + case 'v': + verbose = B_TRUE; + break; + case 'z': + break; + default: + goto err; + } + } + + if (format == NULL || proxy_port == NULL) { + goto err; + } + + if (get_portnum(proxy_port, NULL) == 0) { + (void) fprintf(stderr, + gettext("Error: Invalid proxy port value %s\n"), + proxy_port); + goto err; + } + + if (strcmp(format, "pkcs11") == 0) { + if (token_label == NULL || certname == NULL) { + goto err; + } + } else if (strcmp(format, "pkcs12") == 0 || + strcmp(format, "pem") == 0) { + if (filename == NULL) { + goto err; + } + } else { + goto err; + } + + pcnt = argc - optind; + if (pcnt == 1) { + if (strlen(argv[optind]) < MAX_ADRPORT_LEN) { + (void) strcpy(address_port, argv[optind]); + } else { + (void) fprintf(stderr, gettext( + "argument too long -- %s\n"), + argv[optind]); + return (FAILURE); + } + } else if (pcnt == 2) { + if ((len = strlen(argv[optind])) + + (strlen(argv[optind + 1])) < MAX_ADRPORT_LEN) { + (void) strcpy(address_port, argv[optind]); + address_port[len] = ' '; + (void) strcpy(address_port + len + 1, argv[optind + 1]); + } else { + (void) fprintf(stderr, gettext( + "arguments too long -- %s %s\n"), + argv[optind], argv[optind + 1]); + return (FAILURE); + } + } else { + goto err; + } + + /* + * We need to create the kssladm command line in + * the SMF instance from the current arguments. + * + * Construct a buffer with all the arguments except + * the -u argument. We have to quote the string arguments, + * -T and -C, as they can contain white space. + */ + len = 0; + for (i = 1; i < optind; i++) { + len += strlen(argv[i]) + 3; + } + + if ((buf = malloc(len)) == NULL) { + return (FAILURE); + } + + ptr = buf; + quote_next = B_FALSE; + for (i = 1; i < optind; i++) { + int arglen = strlen(argv[i]) + 1; + + if (strncmp(argv[i], "-u", 2) == 0) { + i++; + continue; + } + + if (quote_next) { + (void) snprintf(ptr, len, "\"%s\" ", argv[i]); + quote_next = B_FALSE; + arglen += 2; + } else { + (void) snprintf(ptr, len, "%s ", argv[i]); + } + + quote_next = (strncmp(argv[i], "-T", 2) == 0 || + strncmp(argv[i], "-C", 2) == 0); + + ptr += arglen; + len -= arglen; + } + KSSL_DEBUG("buf=%s\n", buf); + + instance_name = create_instance_name(address_port, + &inaddr_any_name, B_TRUE); + if (instance_name == NULL || inaddr_any_name == NULL) { + free(buf); + return (FAILURE); + } + KSSL_DEBUG("instance_name=%s\n", instance_name); + KSSL_DEBUG("inaddr_any_name=%s\n", inaddr_any_name); + + if (username == NULL) + username = "root"; + status = create_service(instance_name, address_port, + buf, username, inaddr_any_name); + if (status == INSTANCE_OTHER_EXISTS || status == INSTANCE_ANY_EXISTS) { + if (status == INSTANCE_ANY_EXISTS && + (strcmp(instance_name, inaddr_any_name) != SUCCESS)) { + /* + * The following could result in a misconfiguration. + * Better bail out with an error. + */ + (void) fprintf(stderr, + gettext("Error: INADDR_ANY instance exists." + " Can not create a new instance %s.\n"), + instance_name); + free(instance_name); + free(inaddr_any_name); + free(buf); + return (status); + } + + /* + * Delete the existing instance and create a new instance + * with the supplied arguments. + */ + KSSL_DEBUG("Deleting duplicate instance\n"); + if (delete_instance(instance_name) != SUCCESS) { + (void) fprintf(stderr, + gettext( + "Error: Can not delete existing instance %s.\n"), + instance_name); + } else { + (void) fprintf(stdout, gettext( + "Note: reconfiguring the existing instance %s.\n"), + instance_name); + status = create_service(instance_name, address_port, + buf, username, inaddr_any_name); + } + } + + free(instance_name); + free(inaddr_any_name); + free(buf); + return (status); + +err: + usage_create(B_TRUE); + return (ERROR_USAGE); +} diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/cmd/cmd-inet/usr.sbin/kssl/ksslcfg/ksslcfg_delete.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/cmd-inet/usr.sbin/kssl/ksslcfg/ksslcfg_delete.c Sat Nov 12 18:58:05 2005 -0800 @@ -0,0 +1,296 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include +#include "ksslcfg.h" + +void +usage_delete(boolean_t do_print) +{ + if (do_print) + (void) fprintf(stderr, gettext("Usage:\n")); + (void) fprintf(stderr, + "ksslcfg delete [-v] [] \n"); +} + +#define DEFAULT_TIMEOUT 60000000 +#define INIT_WAIT_USECS 50000 + +void +wait_till_to(char *fmri) +{ + char *state; + useconds_t max; + useconds_t usecs; + uint64_t *cp = NULL; + scf_simple_prop_t *sp = NULL; + + max = DEFAULT_TIMEOUT; + + if (((sp = scf_simple_prop_get(NULL, fmri, "stop", + SCF_PROPERTY_TIMEOUT)) != NULL) && + ((cp = scf_simple_prop_next_count(sp)) != NULL) && (*cp != 0)) + max = (*cp) * 1000000; /* convert to usecs */ + + if (sp != NULL) + scf_simple_prop_free(sp); + + for (usecs = INIT_WAIT_USECS; max > 0; max -= usecs) { + /* incremental wait */ + usecs *= 2; + usecs = (usecs > max) ? max : usecs; + + (void) usleep(usecs); + + /* Check state after the wait */ + if ((state = smf_get_state(fmri)) != NULL) { + if (strcmp(state, "disabled") == 0) + return; + } + } + + (void) fprintf(stderr, gettext("Warning: delete %s timed out.\n"), + fmri); +} + +int +delete_instance(const char *instance_name) +{ + int status = FAILURE; + char *buf; + boolean_t errflag = B_FALSE; + ssize_t max_fmri_len; + scf_scope_t *scope; + scf_service_t *svc; + scf_handle_t *handle; + scf_instance_t *instance; + + handle = scf_handle_create(SCF_VERSION); + if (handle == NULL) { + errflag = B_TRUE; + KSSL_DEBUG("scf_handle_create failed: %s\n", + scf_strerror(scf_error())); + goto out1; + } + KSSL_DEBUG("scf_handle_create succeeded\n"); + + if (scf_handle_bind(handle) == -1) { + errflag = B_TRUE; + KSSL_DEBUG("scf_handle_bind failed: %s\n", + scf_strerror(scf_error())); + goto out1; + } + KSSL_DEBUG("scf_handle_bind succeeded\n"); + + if ((scope = scf_scope_create(handle)) == NULL) { + errflag = B_TRUE; + KSSL_DEBUG("scf_scope_create failed: %s\n", + scf_strerror(scf_error())); + goto out2; + } + KSSL_DEBUG("scf_scope_create succeeded\n"); + + if ((svc = scf_service_create(handle)) == NULL) { + errflag = B_TRUE; + KSSL_DEBUG("scf_service_create failed: %s\n", + scf_strerror(scf_error())); + goto out3; + } + KSSL_DEBUG("scf_service_create succeeded\n"); + + if (scf_handle_get_scope(handle, SCF_SCOPE_LOCAL, scope) == -1) { + errflag = B_TRUE; + KSSL_DEBUG("scf_handle_get_scope failed: %s\n", + scf_strerror(scf_error())); + goto out4; + } + KSSL_DEBUG("scf_handle_get_scope succeeded\n"); + + if (scf_scope_get_service(scope, SERVICE_NAME, svc) < 0) { + scf_error_t scf_errnum = scf_error(); + + if (scf_errnum != SCF_ERROR_NOT_FOUND) { + errflag = B_TRUE; + KSSL_DEBUG( + "ERROR scf_scope_get_service failed: %s\n", + scf_strerror(scf_errnum)); + } + goto out4; + } else { + KSSL_DEBUG("scf_scope_get_service succeeded\n"); + } + + instance = scf_instance_create(handle); + if (instance == NULL) { + errflag = B_TRUE; + KSSL_DEBUG("scf_instance_create failed: %s\n", + scf_strerror(scf_error())); + goto out4; + } + + if (scf_service_get_instance(svc, instance_name, instance) != 0) { + scf_error_t scf_errnum = scf_error(); + + if (scf_errnum == SCF_ERROR_NOT_FOUND) { + status = SUCCESS; + } else { + errflag = B_TRUE; + KSSL_DEBUG( + "ERROR scf_scope_get_service failed: %s\n", + scf_strerror(scf_errnum)); + } + scf_instance_destroy(instance); + goto out4; + } + + max_fmri_len = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH); + if ((buf = malloc(max_fmri_len + 1)) == NULL) + goto out4; + + if (scf_instance_to_fmri(instance, buf, max_fmri_len + 1) > 0) { + char *state; + + KSSL_DEBUG("instance_fmri=%s\n", buf); + state = smf_get_state(buf); + if (state) + KSSL_DEBUG("state=%s\n", state); + if (state && strcmp(state, "online") == 0) { + if (smf_disable_instance(buf, 0) != 0) { + errflag = B_TRUE; + KSSL_DEBUG( + "smf_disable_instance failed: %s\n", + scf_strerror(scf_error())); + } else { + /* + * Wait for some time till timeout to avoid + * a race with scf_instance_delete() below. + */ + wait_till_to(buf); + } + } + } + + if (scf_instance_delete(instance) != 0) { + errflag = B_TRUE; + KSSL_DEBUG( + "ERROR scf_instance_delete failed: %s\n", + scf_strerror(scf_error())); + goto out4; + } else { + KSSL_DEBUG("deleted %s\n", instance_name); + } + + status = SUCCESS; + +out4: + scf_service_destroy(svc); +out3: + scf_scope_destroy(scope); +out2: + (void) scf_handle_unbind(handle); +out1: + if (handle != NULL) + scf_handle_destroy(handle); + if (errflag) + (void) fprintf(stderr, gettext( + "Unexpected fatal libscf error: %s. Exiting.\n"), + scf_strerror(scf_error())); + return (status); +} + +int +do_delete(int argc, char *argv[]) +{ + char c; + int status, len, pcnt; + char address_port[MAX_ADRPORT_LEN + 1]; + char *instance_name; + + if (argc < 3) { + goto err; + } + + argc -= 1; + argv += 1; + + while ((c = getopt(argc, argv, "v")) != -1) { + switch (c) { + case 'v': + verbose = B_TRUE; + break; + default: + goto err; + } + } + + pcnt = argc - optind; + if (pcnt == 1) { + if (strlen(argv[optind]) < MAX_ADRPORT_LEN) { + (void) strcpy(address_port, argv[optind]); + } else { + (void) fprintf(stderr, gettext( + "argument too long -- %s\n"), + argv[optind]); + return (FAILURE); + } + } else if (pcnt == 2) { + if ((len = strlen(argv[optind])) + + (strlen(argv[optind + 1])) < MAX_ADRPORT_LEN) { + (void) strcpy(address_port, argv[optind]); + address_port[len] = ' '; + (void) strcpy(address_port + len + 1, argv[optind + 1]); + } else { + (void) fprintf(stderr, gettext( + "arguments too long -- %s %s\n"), + argv[optind], argv[optind + 1]); + return (FAILURE); + } + } else { + goto err; + } + + instance_name = create_instance_name(address_port, NULL, B_FALSE); + if (instance_name == NULL) { + return (FAILURE); + } + + KSSL_DEBUG("instance_name=%s\n", instance_name); + status = delete_instance(instance_name); + free(instance_name); + + return (status); + +err: + usage_delete(B_TRUE); + return (ERROR_USAGE); +} diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/cmd/devfsadm/misc_link.c --- a/usr/src/cmd/devfsadm/misc_link.c Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/cmd/devfsadm/misc_link.c Sat Nov 12 18:58:05 2005 -0800 @@ -95,7 +95,7 @@ "(^eeprom$)|(^ptsl$)|(^mm$)|(^wc$)|(^dump$)|(^cn$)|(^lo$)|(^ptm$)|" "(^ptc$)|(^openeepr$)|(^poll$)|(^sysmsg$)|(^random$)|(^trapstat$)|" "(^cryptoadm$)|(^crypto$)|(^pool$)|(^poolctl$)|(^bl$)|(^kmdb$)|" - "(^sysevent$)", + "(^sysevent$)|(^kssl$)", TYPE_EXACT | DRV_RE, ILEVEL_1, minor_name }, { "pseudo", "ddi_pseudo", diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/lib/libbsm/audit_event.txt --- a/usr/src/lib/libbsm/audit_event.txt Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/lib/libbsm/audit_event.txt Sat Nov 12 18:58:05 2005 -0800 @@ -326,6 +326,7 @@ 290:AUE_MODDEVPLCY:modctl(2) - configure device policy:as 291:AUE_MODADDPRIV:modctl(2) - configure additional privilege:as 292:AUE_CRYPTOADM:kernel cryptographic framework:as +293:AUE_CONFIGSSL:configure kernel SSL:as # # user level audit events # diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/lib/libsecdb/exec_attr.txt --- a/usr/src/lib/libsecdb/exec_attr.txt Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/lib/libsecdb/exec_attr.txt Sat Nov 12 18:58:05 2005 -0800 @@ -198,6 +198,7 @@ Network Security:solaris:cmd:::/usr/sbin/ipsecconf:privs=sys_net_config Network Security:solaris:cmd:::/usr/sbin/ipseckey:privs=sys_net_config Network Security:solaris:cmd:::/usr/sbin/ipsecalgs:privs=sys_net_config +Network Security:solaris:cmd:::/usr/sbin/ksslcfg:euid=0 Network Security:suser:cmd:::/usr/bin/ssh-keygen:uid=0;gid=sys Network Security:suser:cmd:::/usr/lib/inet/certdb:euid=0 Network Security:suser:cmd:::/usr/lib/inet/certlocal:euid=0 diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/pkgdefs/SUNWckr/prototype_com --- a/usr/src/pkgdefs/SUNWckr/prototype_com Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/pkgdefs/SUNWckr/prototype_com Sat Nov 12 18:58:05 2005 -0800 @@ -85,6 +85,7 @@ f none kernel/drv/iwscn.conf 644 root sys f none kernel/drv/keysock.conf 644 root sys f none kernel/drv/kmdb.conf 644 root sys +f none kernel/drv/kssl.conf 644 root sys f none kernel/drv/llc1.conf 644 root sys f none kernel/drv/lofi.conf 644 root sys e preserve kernel/drv/log.conf 644 root sys diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/pkgdefs/SUNWckr/prototype_i386 --- a/usr/src/pkgdefs/SUNWckr/prototype_i386 Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/pkgdefs/SUNWckr/prototype_i386 Sat Nov 12 18:58:05 2005 -0800 @@ -84,6 +84,7 @@ f none kernel/drv/iwscn 755 root sys f none kernel/drv/keysock 755 root sys f none kernel/drv/kmdb 755 root sys +f none kernel/drv/kssl 755 root sys f none kernel/drv/llc1 755 root sys f none kernel/drv/lofi 755 root sys f none kernel/drv/log 755 root sys @@ -252,6 +253,7 @@ f none kernel/drv/amd64/iwscn 755 root sys f none kernel/drv/amd64/keysock 755 root sys f none kernel/drv/amd64/kmdb 755 root sys +f none kernel/drv/amd64/kssl 755 root sys f none kernel/drv/amd64/llc1 755 root sys f none kernel/drv/amd64/lofi 755 root sys f none kernel/drv/amd64/log 755 root sys diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/pkgdefs/SUNWckr/prototype_sparc --- a/usr/src/pkgdefs/SUNWckr/prototype_sparc Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/pkgdefs/SUNWckr/prototype_sparc Sat Nov 12 18:58:05 2005 -0800 @@ -91,6 +91,7 @@ f none kernel/drv/sparcv9/kb8042 755 root sys f none kernel/drv/sparcv9/keysock 755 root sys f none kernel/drv/sparcv9/kmdb 755 root sys +f none kernel/drv/sparcv9/kssl 755 root sys f none kernel/drv/sparcv9/llc1 755 root sys f none kernel/drv/sparcv9/lofi 755 root sys f none kernel/drv/sparcv9/log 755 root sys diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/pkgdefs/SUNWcsr/prototype_com --- a/usr/src/pkgdefs/SUNWcsr/prototype_com Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/pkgdefs/SUNWcsr/prototype_com Sat Nov 12 18:58:05 2005 -0800 @@ -474,6 +474,8 @@ f manifest var/svc/manifest/network/dns/client.xml 0444 root sys d none var/svc/manifest/network/ldap 0755 root sys f manifest var/svc/manifest/network/ldap/client.xml 0444 root sys +d none var/svc/manifest/network/ssl 0755 root sys +f manifest var/svc/manifest/network/ssl/kssl-proxy.xml 0444 root sys d none var/svc/manifest/platform 755 root sys d none var/svc/manifest/site 755 root sys d none var/svc/manifest/system 755 root sys diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/pkgdefs/SUNWcsu/prototype_com --- a/usr/src/pkgdefs/SUNWcsu/prototype_com Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/pkgdefs/SUNWcsu/prototype_com Sat Nov 12 18:58:05 2005 -0800 @@ -548,6 +548,7 @@ f none usr/lib/inet/inetd 555 root bin f none usr/lib/intrd 555 root bin f none usr/lib/isaexec 555 root bin +f none usr/lib/kssladm 555 root bin s none usr/lib/ld.so.1=../../lib/ld.so.1 d none usr/lib/locale 755 root bin d none usr/lib/locale/C 755 root bin @@ -755,6 +756,7 @@ f none usr/sbin/install 555 root bin f none usr/sbin/keyserv 555 root sys f none usr/sbin/killall 555 root bin +f none usr/sbin/ksslcfg 555 root bin s none usr/sbin/labelit=./clri f none usr/sbin/link 555 root bin s none usr/sbin/lockfs=../lib/fs/ufs/lockfs diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/pkgdefs/SUNWhea/prototype_com --- a/usr/src/pkgdefs/SUNWhea/prototype_com Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/pkgdefs/SUNWhea/prototype_com Sat Nov 12 18:58:05 2005 -0800 @@ -149,6 +149,8 @@ f none usr/include/inet/snmpcom.h 644 root bin f none usr/include/inet/tcp.h 644 root bin f none usr/include/inet/tcp_sack.h 644 root bin +d none usr/include/inet/kssl 755 root bin +f none usr/include/inet/kssl/ksslapi.h 644 root bin f none usr/include/inttypes.h 644 root bin f none usr/include/ipmp.h 644 root bin f none usr/include/ipmp_mpathd.h 644 root bin diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/pkgdefs/etc/exception_list_i386 --- a/usr/src/pkgdefs/etc/exception_list_i386 Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/pkgdefs/etc/exception_list_i386 Sat Nov 12 18:58:05 2005 -0800 @@ -524,6 +524,11 @@ usr/include/inet/nca/ncandd.h i386 usr/include/inet/nca/ncaio.h i386 # +usr/include/inet/kssl i386 +usr/include/inet/kssl/kssl.h i386 +usr/include/inet/kssl/ksslimpl.h i386 +usr/include/inet/kssl/ksslproto.h i386 +# # other contents (packages removed in source product) usr/bin/des i386 usr/include/des/desdata.h i386 diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/pkgdefs/etc/exception_list_sparc --- a/usr/src/pkgdefs/etc/exception_list_sparc Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/pkgdefs/etc/exception_list_sparc Sat Nov 12 18:58:05 2005 -0800 @@ -531,6 +531,10 @@ usr/include/inet/nca/ncalogd.h sparc usr/include/inet/nca/ncandd.h sparc usr/include/inet/nca/ncaio.h sparc +usr/include/inet/kssl sparc +usr/include/inet/kssl/kssl.h sparc +usr/include/inet/kssl/ksslimpl.h sparc +usr/include/inet/kssl/ksslproto.h sparc # # other contents (packages removed in source product) usr/bin/des sparc diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/Makefile --- a/usr/src/uts/Makefile Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/uts/Makefile Sat Nov 12 18:58:05 2005 -0800 @@ -92,8 +92,8 @@ # COMMON_HDRDIRS= common/des common/fs common/gssapi common/inet common/net \ common/netinet common/nfs common/rpc common/sys common/vm \ - common/c2 common/pcmcia/sys common/rpcsvc common/inet/nca \ - common/ipp + common/c2 common/pcmcia/sys common/rpcsvc common/inet/kssl \ + common/inet/nca common/ipp sparc_HDRDIRS= sun/sys i386_HDRDIRS= i86pc/vm diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/common/Makefile.files --- a/usr/src/uts/common/Makefile.files Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/uts/common/Makefile.files Sat Nov 12 18:58:05 2005 -0800 @@ -417,7 +417,8 @@ RTS_OBJS += rtsddi.o rts.o rts_opt_data.o -IP_TCP_OBJS = tcp.o tcp_trace.o tcp_opt_data.o tcp_sack.o tcp_fusion.o +IP_TCP_OBJS = tcp.o tcp_fusion.o tcp_kssl.o tcp_opt_data.o tcp_sack.o \ + tcp_trace.o IP_UDP_OBJS = udp.o udp_opt_data.o IP_SCTP_OBJS = sctp_crc32.o sctp.o sctp_opt_data.o sctp_output.o \ sctp_init.o sctp_input.o sctp_cookie.o \ @@ -805,8 +806,9 @@ SOCK_OBJS += socksubr.o sockvfsops.o sockvnops.o \ socksyscalls.o socktpi.o sockstr.o ncafs.o \ - socksctp.o socksctpsubr.o socksctpvnops.o nl7c.o \ - nl7curi.o nl7chttp.o nl7clogd.o nl7cnca.o + socksctp.o socksctpsubr.o socksctpvnops.o sockssl.o \ + nl7c.o nl7curi.o nl7chttp.o nl7clogd.o \ + nl7cnca.o TMPFS_OBJS += tmp_dir.o tmp_subr.o tmp_tnode.o tmp_vfsops.o \ tmp_vnops.o @@ -1040,6 +1042,11 @@ SWRANDPROV_OBJS += swrand.o # +# kernel SSL +# +KSSL_OBJS += kssl.o ksslioctl.o ksslapi.o ksslrec.o + +# # misc. modules # AMSRC1_OBJS += am_src1.o diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/common/Makefile.rules --- a/usr/src/uts/common/Makefile.rules Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/uts/common/Makefile.rules Sat Nov 12 18:58:05 2005 -0800 @@ -349,6 +349,10 @@ $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) +$(OBJS_DIR)/%.o: $(UTSBASE)/common/inet/kssl/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + $(OBJS_DIR)/%.o: $(UTSBASE)/common/inet/sctp/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) @@ -1136,6 +1140,9 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/common/inet/ipf/%.c @($(LHEAD) $(LINT.c) $(IPFFLAG2) $< $(LTAIL)) +$(LINTS_DIR)/%.ln: $(UTSBASE)/common/inet/kssl/%.c + @($(LHEAD) $(LINT.c) $< $(LTAIL)) + $(LINTS_DIR)/%.ln: $(UTSBASE)/common/inet/udp/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/common/c2/audit.c --- a/usr/src/uts/common/c2/audit.c Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/uts/common/c2/audit.c Sat Nov 12 18:58:05 2005 -0800 @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -63,6 +63,7 @@ #include /* for servicing_interrupt() */ #include #include +#include static void add_return_token(caddr_t *, unsigned int scid, int err, int rval); @@ -2274,3 +2275,77 @@ au_close(kctx, (caddr_t *)&ad, AU_OK, AUE_CRYPTOADM, 0); } + +/* + * Audit the kernel SSL administration command. The address and the + * port number for the SSL instance, and the proxy port are put in the + * audit trail. + */ +void +audit_kssl(int cmd, void *params, int error) +{ + cred_t *cr = CRED(); + t_audit_data_t *tad; + token_t *ad = NULL; + const auditinfo_addr_t *ainfo = crgetauinfo(cr); + au_kcontext_t *kctx = SET_KCTX_PZ; + + ASSERT(kctx != NULL); + tad = U2A(u); + + if (ainfo == NULL) + return; + + tad->tad_event = AUE_CONFIGKSSL; + + if (audit_success(kctx, tad, error) != AU_OK) + return; + + /* Add a subject token */ + AUDIT_SETSUBJ((caddr_t *)&ad, cr, ainfo); + + /* add an optional group token */ + AUDIT_SETGROUP((caddr_t *)&ad, cr, kctx); + + switch (cmd) { + case KSSL_ADD_ENTRY: { + char buf[32]; + kssl_params_t *kp = (kssl_params_t *)params; + struct sockaddr_in *saddr = &(kp->kssl_addr); + + au_write((caddr_t *)&ad, au_to_text("op=KSSL_ADD_ENTRY")); + au_write((caddr_t *)&ad, au_to_in_addr(&(saddr->sin_addr))); + (void) snprintf(buf, sizeof (buf), "SSL port=%d", + saddr->sin_port); + au_write((caddr_t *)&ad, au_to_text(buf)); + + (void) snprintf(buf, sizeof (buf), "proxy port=%d", + kp->kssl_proxy_port); + au_write((caddr_t *)&ad, au_to_text(buf)); + break; + } + + case KSSL_DELETE_ENTRY: { + char buf[32]; + struct sockaddr_in *saddr = (struct sockaddr_in *)params; + + au_write((caddr_t *)&ad, au_to_text("op=KSSL_DELETE_ENTRY")); + au_write((caddr_t *)&ad, au_to_in_addr(&(saddr->sin_addr))); + (void) snprintf(buf, sizeof (buf), "SSL port=%d", + saddr->sin_port); + au_write((caddr_t *)&ad, au_to_text(buf)); + break; + } + + default: + return; + } + + /* add a return token */ + add_return_token((caddr_t *)&ad, tad->tad_scid, error, 0); + + AS_INC(as_generated, 1, kctx); + AS_INC(as_kernel, 1, kctx); + + au_close(kctx, (caddr_t *)&ad, AU_OK, AUE_CONFIGKSSL, 0); +} diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/common/c2/audit.h --- a/usr/src/uts/common/c2/audit.h Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/uts/common/c2/audit.h Sat Nov 12 18:58:05 2005 -0800 @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -579,6 +579,7 @@ void audit_setppriv(int, int, const struct priv_set *, const cred_t *); void audit_devpolicy(int, const struct devplcysys *); void audit_update_context(proc_t *, cred_t *); +void audit_kssl(int, void *, int); #endif diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/common/c2/audit_kevents.h --- a/usr/src/uts/common/c2/audit_kevents.h Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/uts/common/c2/audit_kevents.h Sat Nov 12 18:58:05 2005 -0800 @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -333,6 +333,7 @@ #define AUE_MODDEVPLCY 290 /* =ad modctl(2) */ #define AUE_MODADDPRIV 291 /* =ad modctl(2) */ #define AUE_CRYPTOADM 292 /* =as kernel cryptographic framework */ +#define AUE_CONFIGKSSL 293 /* =as kernel SSL */ /* * Maximum number of kernel events in the event to class table diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/common/fs/sockfs/sockssl.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/fs/sockfs/sockssl.c Sat Nov 12 18:58:05 2005 -0800 @@ -0,0 +1,116 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + + +/* + * This routine is registered with the stream head to be called by kstrgetmsg() + * with every packet received on the read queue, and before copying its + * content to user buffers. kstrgetmsg() calls only once with the same + * message. + * If the message is successfully procssed, then it is returned. + * A failed message will be freed. + */ +/* ARGSUSED */ +mblk_t * +strsock_kssl_input(vnode_t *vp, mblk_t *mp, + strwakeup_t *wakeups, strsigset_t *firstmsgsigs, + strsigset_t *allmsgsigs, strpollset_t *pollwakeups) +{ + struct sonode *so = VTOSO(vp); + kssl_ctx_t kssl_ctx = so->so_kssl_ctx; + kssl_cmd_t kssl_cmd; + mblk_t *out; + + dprintso(so, 1, ("strsock_kssl_input(%p, %p)\n", vp, mp)); + + ASSERT(!(DB_FLAGS(mp) & DBLK_COOKED)); + + kssl_cmd = kssl_handle_record(kssl_ctx, &mp, &out); + + switch (kssl_cmd) { + case KSSL_CMD_NONE: + return (NULL); + + case KSSL_CMD_DELIVER_PROXY: + return (mp); + + case KSSL_CMD_SEND: { + ASSERT(out != NULL); + + putnext(vp->v_stream->sd_wrq, out); + } + /* FALLTHU */ + default: + /* transient error. */ + return (NULL); + } +} + +/* + * This routine is registered with the stream head be called by + * kstrmakedata() with every packet sent downstreams. + * If the message is successfully procssed, then it is returned. + */ +/* ARGSUSED */ +mblk_t * +strsock_kssl_output(vnode_t *vp, mblk_t *mp, + strwakeup_t *wakeups, strsigset_t *firstmsgsigs, + strsigset_t *allmsgsigs, strpollset_t *pollwakeups) +{ + struct sonode *so = VTOSO(vp); + kssl_ctx_t kssl_ctx = so->so_kssl_ctx; + mblk_t *recmp; + + dprintso(so, 1, ("strsock_kssl_output(%p, %p)\n", vp, mp)); + + if ((recmp = kssl_build_record(kssl_ctx, mp)) == NULL) { + /* The caller will free the bogus message */ + return (NULL); + } + return (recmp); +} diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/common/fs/sockfs/sockstr.c --- a/usr/src/uts/common/fs/sockfs/sockstr.c Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/uts/common/fs/sockfs/sockstr.c Sat Nov 12 18:58:05 2005 -0800 @@ -65,6 +65,8 @@ #define _SUN_TPI_VERSION 2 #include +#include + #include int so_default_version = SOV_SOCKSTREAM; @@ -1202,6 +1204,20 @@ } so->so_error = ECONNABORTED; mutex_exit(&so->so_lock); + + /* + * T_KSSL_PROXY_CONN_IND may carry a handle for + * an SSL context, and needs to be released. + */ + if ((tci->PRIM_type == T_SSL_PROXY_CONN_IND) && + (mp->b_cont != NULL)) { + kssl_ctx_t kssl_ctx; + + ASSERT(MBLKL(mp->b_cont) == + sizeof (kssl_ctx_t)); + kssl_ctx = *((kssl_ctx_t *)mp->b_cont->b_rptr); + kssl_release_ctx(kssl_ctx); + } freemsg(mp); return (0); } @@ -2148,6 +2164,11 @@ return (NULL); } + /* + * Extra processing in case of an SSL proxy, before queuing or + * forwarding to the fallback endpoint + */ + case T_SSL_PROXY_CONN_IND: case T_CONN_IND: /* * Verify the min size and queue the message on @@ -2171,6 +2192,28 @@ freemsg(mp); return (NULL); } + + if (tpr->type == T_SSL_PROXY_CONN_IND && mp->b_cont == NULL) { + /* No context: need to fall back */ + struct sonode *fbso; + stdata_t *fbstp; + + tpr->type = T_CONN_IND; + + fbso = kssl_find_fallback(so->so_kssl_ent); + + /* + * No fallback: the remote will timeout and + * disconnect. + */ + if (fbso == NULL) { + freemsg(mp); + return (NULL); + } + fbstp = SOTOV(fbso)->v_stream; + qreply(fbstp->sd_wrq->q_next, mp); + return (NULL); + } soqueueconnind(so, mp); *allmsgsigs = S_INPUT | S_RDNORM; *pollwakeups = POLLIN | POLLRDNORM; diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/common/fs/sockfs/socktpi.c --- a/usr/src/uts/common/fs/sockfs/socktpi.c Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/uts/common/fs/sockfs/socktpi.c Sat Nov 12 18:58:05 2005 -0800 @@ -78,6 +78,8 @@ #include #include +#include + /* * Possible failures when memory can't be allocated. The documented behavior: * @@ -174,6 +176,12 @@ extern void *nl7c_add_addr(void *, t_uscalar_t); extern void nl7c_listener_addr(void *, queue_t *); +/* Sockets acting as an in-kernel SSL proxy */ +extern mblk_t *strsock_kssl_input(vnode_t *, mblk_t *, strwakeup_t *, + strsigset_t *, strsigset_t *, strpollset_t *); +extern mblk_t *strsock_kssl_output(vnode_t *, mblk_t *, strwakeup_t *, + strsigset_t *, strsigset_t *, strpollset_t *); + static int sotpi_unbind(struct sonode *, int); /* TPI sockfs sonode operations */ @@ -289,6 +297,12 @@ version = so_default_version; so->so_version = (short)version; + + /* Initialize the kernel SSL proxy fields */ + so->so_kssl_type = KSSL_NO_PROXY; + so->so_kssl_ent = NULL; + so->so_kssl_ctx = NULL; + return (so); } @@ -773,9 +787,37 @@ mp = soallocproto2(&bind_req, sizeof (bind_req), addr, addrlen, 0, _ALLOC_SLEEP); so->so_state &= ~SS_LADDR_VALID; + /* Done using so_laddr_sa - can drop the lock */ mutex_exit(&so->so_lock); + /* + * Intercept the bind_req message here to check if this
+ * was configured as an SSL proxy server, or if another endpoint was + * already configured to act as a proxy for us. + */ + if ((so->so_family == AF_INET || so->so_family == AF_INET6) && + so->so_type == SOCK_STREAM) { + + if (so->so_kssl_ent != NULL) { + kssl_release_ent(so->so_kssl_ent, so, so->so_kssl_type); + so->so_kssl_ent = NULL; + } + + so->so_kssl_type = kssl_check_proxy(mp, so, &so->so_kssl_ent); + switch (so->so_kssl_type) { + case KSSL_NO_PROXY: + break; + + case KSSL_HAS_PROXY: + mutex_enter(&so->so_lock); + goto skip_transport; + + case KSSL_IS_PROXY: + break; + } + } + error = kstrputmsg(SOTOV(so), mp, NULL, 0, 0, MSG_BAND|MSG_HOLDSIG|MSG_IGNERROR, 0); if (error) { @@ -791,6 +833,7 @@ eprintsoline(so, error); goto done; } +skip_transport: ASSERT(mp); /* * Even if some TPI message (e.g. T_DISCON_IND) was received in @@ -1155,7 +1198,18 @@ vnode_t *vp; if ((vp = so->so_ux_bound_vp) != NULL) { - ASSERT(vp->v_stream); + + /* Undo any SSL proxy setup */ + if ((so->so_family == AF_INET || + so->so_family == AF_INET6) && + (so->so_type == SOCK_STREAM) && + (so->so_kssl_ent != NULL)) { + kssl_release_ent(so->so_kssl_ent, so, + so->so_kssl_type); + so->so_kssl_ent = NULL; + so->so_kssl_type = KSSL_NO_PROXY; + } + so->so_ux_bound_vp = NULL; vn_rele_stream(vp); } @@ -1164,6 +1218,7 @@ } so->so_state &= ~(SS_ISBOUND|SS_ACCEPTCONN|SS_LADDR_VALID); done: + /* If the caller held the lock don't release it here */ ASSERT(MUTEX_HELD(&so->so_lock)); ASSERT(so->so_flag & SOLOCKED); @@ -1356,7 +1411,7 @@ struct T_conn_ind *conn_ind; struct T_conn_res *conn_res; int error = 0; - mblk_t *mp; + mblk_t *mp, *ctxmp; struct sonode *nso; vnode_t *nvp; void *src; @@ -1384,6 +1439,8 @@ ASSERT(mp); conn_ind = (struct T_conn_ind *)mp->b_rptr; + ctxmp = mp->b_cont; + /* * Save SEQ_number for error paths. */ @@ -1476,6 +1533,23 @@ } nvp = SOTOV(nso); + /* + * If the transport sent up an SSL connection context, then attach + * it the new socket, and set the (sd_wputdatafunc)() and + * (sd_rputdatafunc)() stream head hooks to intercept and process + * SSL records. + */ + if (ctxmp != NULL) { + /* + * This kssl_ctx_t is already held for us by the transport. + * So, we don't need to do a kssl_hold_ctx() here. + */ + nso->so_kssl_ctx = *((kssl_ctx_t *)ctxmp->b_rptr); + freemsg(ctxmp); + mp->b_cont = NULL; + strsetrwputdatahooks(nvp, strsock_kssl_input, + strsock_kssl_output); + } #ifdef DEBUG /* * SO_DEBUG is used to trigger the dprint* and eprint* macros thus @@ -4288,6 +4362,7 @@ struct stdata *stp = SOTOV(so)->v_stream; ssize_t iosize, rmax, maxblk; queue_t *tcp_wq = stp->sd_wrq->q_next; + mblk_t *newmp; int error = 0, wflag = 0; ASSERT(so->so_mode & SM_BYTESTREAM); @@ -4305,6 +4380,15 @@ * data to tcp. */ ASSERT(mp != NULL); + if (stp->sd_wputdatafunc != NULL) { + newmp = (stp->sd_wputdatafunc)(SOTOV(so), mp, NULL, + NULL, NULL, NULL); + if (newmp == NULL) { + /* The caller will free mp */ + return (ECOMM); + } + mp = newmp; + } tcp_wput(tcp_wq, mp); return (0); } @@ -4347,6 +4431,15 @@ * to tcp and let the rest be handled in strwrite(). */ ASSERT(error == 0 || error == ENOMEM); + if (stp->sd_wputdatafunc != NULL) { + newmp = (stp->sd_wputdatafunc)(SOTOV(so), mp, NULL, + NULL, NULL, NULL); + if (newmp == NULL) { + /* The caller will free mp */ + return (ECOMM); + } + mp = newmp; + } tcp_wput(tcp_wq, mp); wflag |= NOINTR; diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/common/fs/sockfs/sockvnops.c --- a/usr/src/uts/common/fs/sockfs/sockvnops.c Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/uts/common/fs/sockfs/sockvnops.c Sat Nov 12 18:58:05 2005 -0800 @@ -91,6 +91,8 @@ #include #include +#include + static int socktpi_close(struct vnode *, int, int, offset_t, struct cred *); static int socktpi_read(struct vnode *, struct uio *, int, struct cred *, struct caller_context *); @@ -370,6 +372,19 @@ so->so_ux_bound_vp = NULL; vn_rele_stream(ux_vp); } + if (so->so_family == AF_INET || so->so_family == AF_INET6) { + strsetrwputdatahooks(SOTOV(so), NULL, NULL); + if (so->so_kssl_ent != NULL) { + kssl_release_ent(so->so_kssl_ent, so, + so->so_kssl_type); + so->so_kssl_ent = NULL; + } + if (so->so_kssl_ctx != NULL) { + kssl_release_ctx(so->so_kssl_ctx); + so->so_kssl_ctx = NULL; + } + so->so_kssl_type = KSSL_NO_PROXY; + } error = strclose(vp, flag, cr); vp->v_stream = NULL; mutex_enter(&so->so_lock); diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/common/inet/ip.h --- a/usr/src/uts/common/inet/ip.h Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/uts/common/inet/ip.h Sat Nov 12 18:58:05 2005 -0800 @@ -3068,6 +3068,7 @@ #define SQTAG_UDP_INPUT 33 #define SQTAG_UDP_WPUT 34 #define SQTAG_UDP_OUTPUT 35 +#define SQTAG_TCP_KSSL_INPUT 36 #endif /* _KERNEL */ diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/common/inet/kssl/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/inet/kssl/Makefile Sat Nov 12 18:58:05 2005 -0800 @@ -0,0 +1,53 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# uts/common/inet/kssl/Makefile +# +# include global definitions +include ../../../../Makefile.master + +HDRS= kssl.h ksslapi.h ksslimpl.h ksslproto.h + +ROOTDIRS= $(ROOT)/usr/include/inet/kssl + +ROOTHDRS= $(HDRS:%=$(ROOT)/usr/include/inet/kssl/%) + +CHECKHDRS= $(HDRS:%.h=%.check) + +$(ROOTDIRS)/%: % + $(INS.file) + +.KEEP_STATE: + +.PARALLEL: $(CHECKHDRS) + +install_h: $(ROOTDIRS) $(ROOTHDRS) + +$(ROOTDIRS): + $(INS.dir) + +check: $(CHECKHDRS) diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/common/inet/kssl/kssl.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/inet/kssl/kssl.c Sat Nov 12 18:58:05 2005 -0800 @@ -0,0 +1,580 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * The system call and DDI interface for the kernel SSL module + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kssl.h" +#include "ksslimpl.h" + +/* + * DDI entry points. + */ +static int kssl_attach(dev_info_t *, ddi_attach_cmd_t); +static int kssl_detach(dev_info_t *, ddi_detach_cmd_t); +static int kssl_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); +static int kssl_open(dev_t *, int, int, cred_t *); +static int kssl_close(dev_t, int, int, cred_t *); +static int kssl_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); + +static int kssl_constructor(void *buf, void *arg, int kmflags); +static void kssl_destructor(void *buf, void *arg); + +/* + * Module linkage. + */ +static struct cb_ops cbops = { + kssl_open, /* cb_open */ + kssl_close, /* cb_close */ + nodev, /* cb_strategy */ + nodev, /* cb_print */ + nodev, /* cb_dump */ + nodev, /* cb_read */ + nodev, /* cb_write */ + kssl_ioctl, /* cb_ioctl */ + nodev, /* cb_devmap */ + nodev, /* cb_mmap */ + nodev, /* cb_segmap */ + nochpoll, /* cb_chpoll */ + ddi_prop_op, /* cb_prop_op */ + NULL, /* cb_streamtab */ + D_MP, /* cb_flag */ + CB_REV, /* cb_rev */ + nodev, /* cb_aread */ + nodev, /* cb_awrite */ +}; + +static struct dev_ops devops = { + DEVO_REV, /* devo_rev */ + 0, /* devo_refcnt */ + kssl_getinfo, /* devo_getinfo */ + nulldev, /* devo_identify */ + nulldev, /* devo_probe */ + kssl_attach, /* devo_attach */ + kssl_detach, /* devo_detach */ + nodev, /* devo_reset */ + &cbops, /* devo_cb_ops */ + NULL, /* devo_bus_ops */ + NULL, /* devo_power */ +}; + +static struct modldrv modldrv = { + &mod_driverops, /* drv_modops */ + "Kernel SSL Interface v%I%", /* drv_linkinfo */ + &devops, +}; + +static struct modlinkage modlinkage = { + MODREV_1, /* ml_rev */ + &modldrv, /* ml_linkage */ + NULL +}; + +static dev_info_t *kssl_dip = NULL; + +crypto_mechanism_t rsa_x509_mech = {CRYPTO_MECH_INVALID, NULL, 0}; +crypto_mechanism_t hmac_md5_mech = {CRYPTO_MECH_INVALID, NULL, 0}; +crypto_mechanism_t hmac_sha1_mech = {CRYPTO_MECH_INVALID, NULL, 0}; +crypto_call_flag_t kssl_call_flag = CRYPTO_ALWAYS_QUEUE; + +KSSLCipherDef cipher_defs[] = { /* indexed by SSL3BulkCipher */ + /* type bsize keysz crypto_mech_type_t */ + + {type_stream, 0, 0, CRYPTO_MECH_INVALID}, + + /* mech_type to be initialized with CKM_RC4's */ + {type_stream, 0, 16, CRYPTO_MECH_INVALID}, + + /* mech_type to be initialized with CKM_DES_CBC's */ + {type_block, 8, 8, CRYPTO_MECH_INVALID}, + + /* mech_type to be initialized with CKM_DES3_CBC's */ + {type_block, 8, 24, CRYPTO_MECH_INVALID}, +}; + +int kssl_enabled = 1; +struct kmem_cache *kssl_cache; + +static void kssl_global_init(); +static void kssl_init_mechs(); +static void kssl_event_callback(uint32_t, void *); + +/* + * DDI entry points. + */ +int +_init(void) +{ + return (mod_install(&modlinkage)); +} + +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&modlinkage, modinfop)); +} + +/* ARGSUSED */ +static int +kssl_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) +{ + switch (cmd) { + case DDI_INFO_DEVT2DEVINFO: + *result = kssl_dip; + return (DDI_SUCCESS); + + case DDI_INFO_DEVT2INSTANCE: + *result = (void *)0; + return (DDI_SUCCESS); + } + return (DDI_FAILURE); +} + +static int +kssl_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) +{ + if (cmd != DDI_ATTACH) { + return (DDI_FAILURE); + } + + if (ddi_get_instance(dip) != 0) { + /* we only allow instance 0 to attach */ + return (DDI_FAILURE); + } + + /* create the minor node */ + if (ddi_create_minor_node(dip, "kssl", S_IFCHR, 0, DDI_PSEUDO, 0) != + DDI_SUCCESS) { + cmn_err(CE_WARN, "kssl_attach: failed creating minor node"); + ddi_remove_minor_node(dip, NULL); + return (DDI_FAILURE); + } + + kssl_dip = dip; + + kssl_global_init(); + + return (DDI_SUCCESS); +} + +static int +kssl_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) +{ + if (cmd != DDI_DETACH) + return (DDI_FAILURE); + + if (kssl_entry_tab_nentries != 0) + return (DDI_FAILURE); + + mutex_destroy(&kssl_tab_mutex); + kssl_dip = NULL; + + if (kssl_cache != NULL) { + kmem_cache_destroy(kssl_cache); + } + + ddi_remove_minor_node(dip, NULL); + + return (DDI_SUCCESS); +} + +static crypto_notify_handle_t prov_update_handle = NULL; + +/* ARGSUSED */ +static int +kssl_open(dev_t *devp, int flag, int otyp, cred_t *credp) +{ + if (otyp != OTYP_CHR) + return (ENXIO); + + if (kssl_dip == NULL) + return (ENXIO); + + /* first time here? initialize everything */ + if (rsa_x509_mech.cm_type == CRYPTO_MECH_INVALID) { + kssl_init_mechs(); + prov_update_handle = crypto_notify_events( + kssl_event_callback, CRYPTO_EVENT_PROVIDERS_CHANGE); + } + + /* exclusive opens are not supported */ + if (flag & FEXCL) + return (ENOTSUP); + + return (0); +} + +/* ARGSUSED */ +static int +kssl_close(dev_t dev, int flag, int otyp, cred_t *credp) +{ + return (0); +} + +#define KSSL_MAX_KEYANDCERTS 80000 /* max 64K plus a little margin */ + +/* ARGSUSED */ +static int +kssl_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *c, + int *rval) +{ + int error = EINVAL; + +#define ARG ((caddr_t)arg) + + if (secpolicy_net_config(c, B_FALSE) != 0) { + return (EPERM); + } + + switch (cmd) { + case KSSL_ADD_ENTRY: { + kssl_params_t kssl_params_s, *kssl_params = &kssl_params_s; + uint64_t len; + + if (copyin(ARG, kssl_params, sizeof (kssl_params_s)) != 0) { + return (EFAULT); + } + + len = kssl_params->kssl_params_size; + if (len < sizeof (kssl_params_t) || + len > KSSL_MAX_KEYANDCERTS) { + return (EINVAL); + } + + kssl_params = kmem_alloc(len, KM_SLEEP); + + /* Get the whole structure and parameters in one move */ + if (copyin(ARG, kssl_params, len) != 0) { + kmem_free(kssl_params, len); + return (EFAULT); + } + error = kssl_add_entry(kssl_params); +#ifdef C2_AUDIT + if (audit_active) + audit_kssl(KSSL_ADD_ENTRY, kssl_params, error); +#endif + kmem_free(kssl_params, len); + break; + } + case KSSL_DELETE_ENTRY: { + struct sockaddr_in server_addr; + + if (copyin(ARG, &server_addr, sizeof (server_addr)) != 0) { + return (EFAULT); + } + + error = kssl_delete_entry(&server_addr); +#ifdef C2_AUDIT + if (audit_active) + audit_kssl(KSSL_DELETE_ENTRY, &server_addr, error); +#endif + break; + } + } + + return (error); +} + +#define NUM_MECHS 6 +mech_to_cipher_t mech_to_cipher_tab[NUM_MECHS] = { + {CRYPTO_MECH_INVALID, SUN_CKM_RSA_X_509, + {SSL_RSA_WITH_RC4_128_MD5, SSL_RSA_WITH_RC4_128_SHA, + SSL_RSA_WITH_DES_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA, + SSL_RSA_WITH_NULL_SHA}}, + {CRYPTO_MECH_INVALID, SUN_CKM_MD5_HMAC, {SSL_RSA_WITH_RC4_128_MD5}}, + {CRYPTO_MECH_INVALID, SUN_CKM_SHA1_HMAC, + {SSL_RSA_WITH_RC4_128_SHA, SSL_RSA_WITH_DES_CBC_SHA, + SSL_RSA_WITH_3DES_EDE_CBC_SHA, SSL_RSA_WITH_NULL_SHA}}, + {CRYPTO_MECH_INVALID, SUN_CKM_RC4, + {SSL_RSA_WITH_RC4_128_MD5, SSL_RSA_WITH_RC4_128_SHA}}, + {CRYPTO_MECH_INVALID, SUN_CKM_DES_CBC, {SSL_RSA_WITH_DES_CBC_SHA}}, + {CRYPTO_MECH_INVALID, SUN_CKM_DES3_CBC, + {SSL_RSA_WITH_3DES_EDE_CBC_SHA}} +}; + +static void +kssl_init_mechs() +{ + mech_to_cipher_tab[0].mech = rsa_x509_mech.cm_type = + crypto_mech2id(SUN_CKM_RSA_X_509); + mech_to_cipher_tab[1].mech = hmac_md5_mech.cm_type = + crypto_mech2id(SUN_CKM_MD5_HMAC); + mech_to_cipher_tab[2].mech = hmac_sha1_mech.cm_type = + crypto_mech2id(SUN_CKM_SHA1_HMAC); + + mech_to_cipher_tab[3].mech = cipher_defs[cipher_rc4].mech_type = + crypto_mech2id(SUN_CKM_RC4); + mech_to_cipher_tab[4].mech = cipher_defs[cipher_des].mech_type = + crypto_mech2id(SUN_CKM_DES_CBC); + mech_to_cipher_tab[5].mech = cipher_defs[cipher_3des].mech_type = + crypto_mech2id(SUN_CKM_DES3_CBC); +} + +static int +is_in_suites(uint16_t s, uint16_t *sarray) +{ + int i; + + for (i = 0; i < CIPHER_SUITE_COUNT; i++) { + if (s == sarray[i]) + return (1); + } + + return (0); +} + +static int +is_in_mechlist(char *name, crypto_mech_name_t *mechs, int count) +{ + int i; + + for (i = 0; i < count; i++) { + if (strncmp(name, mechs[i], CRYPTO_MAX_MECH_NAME) == 0) + return (1); + } + + return (0); +} + +/* + * Callback function invoked by the crypto framework when a provider's + * mechanism is available/unavailable. This callback updates entries in the + * kssl_entry_tab[] to make changes to the cipher suites of an entry + * which are affected by the mechansim. + */ +static void +kssl_event_callback(uint32_t event, void *event_arg) +{ + int i, j; + int cnt, rcnt; + uint16_t s; + boolean_t changed; + crypto_mech_name_t *mechs; + uint_t mech_count; + mech_to_cipher_t *mc; + kssl_entry_t *old; + kssl_entry_t *new; + uint16_t tmp_suites[CIPHER_SUITE_COUNT]; + uint16_t dis_list[CIPHER_SUITE_COUNT]; + crypto_notify_event_change_t *prov_change = + (crypto_notify_event_change_t *)event_arg; + + /* ignore events for which we didn't register */ + if (event != CRYPTO_EVENT_PROVIDERS_CHANGE) { + return; + } + + for (i = 0; i < NUM_MECHS; i++) { + mc = &(mech_to_cipher_tab[i]); + if (mc->mech == CRYPTO_MECH_INVALID) + continue; + + /* + * Check if this crypto framework provider mechanism being + * added or removed affects us. + */ + if (strncmp(mc->name, prov_change->ec_mech_name, + CRYPTO_MAX_MECH_NAME) == 0) + break; + } + + if (i == NUM_MECHS) + return; + + mechs = crypto_get_mech_list(&mech_count, KM_SLEEP); + if (mechs == NULL) + return; + + mutex_enter(&kssl_tab_mutex); + + for (i = 0; i < kssl_entry_tab_size; i++) { + if ((old = kssl_entry_tab[i]) == NULL) + continue; + + cnt = 0; + rcnt = 0; + changed = B_FALSE; + for (j = 0; j < CIPHER_SUITE_COUNT; j++) { + tmp_suites[j] = CIPHER_NOTSET; + dis_list[j] = CIPHER_NOTSET; + } + + /* + * We start with the saved cipher suite list for the new entry. + * If a mechanism is disabled, resulting in a cipher suite being + * disabled now, we take it out from the list for the new entry. + * If a mechanism is enabled, resulting in a cipher suite being + * enabled now, we don't need to do any thing. + */ + if (!is_in_mechlist(mc->name, mechs, mech_count)) { + for (j = 0; j < CIPHER_SUITE_COUNT; j++) { + s = mc->kssl_suites[j]; + if (s == 0) + break; + if (is_in_suites(s, old->kssl_saved_Suites)) { + /* Disable this cipher suite */ + if (!is_in_suites(s, dis_list)) + dis_list[cnt++] = s; + } + } + } + + for (j = 0; j < CIPHER_SUITE_COUNT; j++) { + s = old->kssl_saved_Suites[j]; + if (!is_in_suites(s, dis_list)) + tmp_suites[rcnt] = s; + + if (!changed && + (tmp_suites[rcnt] != old->kssl_cipherSuites[rcnt])) + changed = B_TRUE; + rcnt++; + } + + if (changed) { + new = kmem_zalloc(sizeof (kssl_entry_t), KM_NOSLEEP); + if (new == NULL) + continue; + + *new = *old; /* Structure copy */ + old->ke_no_freeall = B_TRUE; + new->ke_refcnt = 0; + new->kssl_cipherSuites_nentries = rcnt; + for (j = 0; j < CIPHER_SUITE_COUNT; j++) + new->kssl_cipherSuites[j] = tmp_suites[j]; + + KSSL_ENTRY_REFHOLD(new); + kssl_entry_tab[i] = new; + KSSL_ENTRY_REFRELE(old); + } + } + + mutex_exit(&kssl_tab_mutex); + crypto_free_mech_list(mechs, mech_count); +} + + +kssl_stats_t *kssl_statp; + +static void +kssl_global_init() +{ + kstat_t *ksp; + + mutex_init(&kssl_tab_mutex, NULL, MUTEX_DRIVER, NULL); + + kssl_cache = kmem_cache_create("kssl_cache", sizeof (ssl_t), + 0, kssl_constructor, kssl_destructor, NULL, NULL, NULL, 0); + + if ((ksp = kstat_create("kssl", 0, "kssl_stats", "crypto", + KSTAT_TYPE_NAMED, sizeof (kssl_stats_t) / sizeof (kstat_named_t), + KSTAT_FLAG_PERSISTENT)) != NULL) { + kssl_statp = ksp->ks_data; + + kstat_named_init(&kssl_statp->sid_cache_lookups, + "kssl_sid_cache_lookups", KSTAT_DATA_UINT64); + kstat_named_init(&kssl_statp->sid_cache_hits, + "kssl_sid_cache_hits", KSTAT_DATA_UINT64); + kstat_named_init(&kssl_statp->sid_uncached, + "kssl_sid_uncached", KSTAT_DATA_UINT64); + + kstat_named_init(&kssl_statp->full_handshakes, + "kssl_full_handshakes", KSTAT_DATA_UINT64); + kstat_named_init(&kssl_statp->resumed_sessions, + "kssl_resumed_sessions", KSTAT_DATA_UINT64); + kstat_named_init(&kssl_statp->fallback_connections, + "kssl_fallback_connections", KSTAT_DATA_UINT64); + kstat_named_init(&kssl_statp->proxy_fallback_failed, + "kssl_proxy_fallback_failed", KSTAT_DATA_UINT64); + kstat_named_init(&kssl_statp->appdata_record_ins, + "kssl_appdata_record_ins", KSTAT_DATA_UINT64); + kstat_named_init(&kssl_statp->appdata_record_outs, + "kssl_appdata_record_outs", KSTAT_DATA_UINT64); + + kstat_named_init(&kssl_statp->alloc_fails, "kssl_alloc_fails", + KSTAT_DATA_UINT64); + kstat_named_init(&kssl_statp->fatal_alerts, + "kssl_fatal_alerts", KSTAT_DATA_UINT64); + kstat_named_init(&kssl_statp->warning_alerts, + "kssl_warning_alerts", KSTAT_DATA_UINT64); + kstat_named_init(&kssl_statp->no_suite_found, + "kssl_no_suite_found", KSTAT_DATA_UINT64); + kstat_named_init(&kssl_statp->compute_mac_failure, + "kssl_compute_mac_failure", KSTAT_DATA_UINT64); + kstat_named_init(&kssl_statp->verify_mac_failure, + "kssl_verify_mac_failure", KSTAT_DATA_UINT64); + kstat_named_init(&kssl_statp->record_decrypt_failure, + "kssl_record_decrypt_failure", KSTAT_DATA_UINT64); + kstat_named_init(&kssl_statp->bad_pre_master_secret, + "kssl_bad_pre_master_secret", KSTAT_DATA_UINT64); + + kstat_install(ksp); + }; +} + +/*ARGSUSED*/ +static int +kssl_constructor(void *buf, void *arg, int kmflags) +{ + ssl_t *ssl = buf; + + mutex_init(&ssl->kssl_lock, NULL, MUTEX_DEFAULT, NULL); + + return (0); +} + +/*ARGSUSED*/ +static void +kssl_destructor(void *buf, void *arg) +{ + ssl_t *ssl = buf; + mutex_destroy(&ssl->kssl_lock); +} diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/common/inet/kssl/kssl.conf --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/inet/kssl/kssl.conf Sat Nov 12 18:58:05 2005 -0800 @@ -0,0 +1,29 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" + +name="kssl" parent="pseudo" instance=0; +ddi-forceattach=1; diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/common/inet/kssl/kssl.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/inet/kssl/kssl.h Sat Nov 12 18:58:05 2005 -0800 @@ -0,0 +1,111 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _INET_KSSL_KSSL_H +#define _INET_KSSL_KSSL_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/* These are re-definition from */ +typedef struct kssl_object_attribute { + uint64_t ka_type; /* attribute type */ + uint32_t ka_value_offset; /* offset to attribute value */ + uint32_t ka_value_len; /* length of attribute value */ +} kssl_object_attribute_t; + + +typedef struct kssl_key { + crypto_key_format_t ks_format; /* format identifier */ + uint32_t ks_count; /* number of attributes */ + uint32_t ks_attrs_offset; /* offset to the attributes */ +} kssl_key_t; + + +typedef struct kssl_certs_s { + uint32_t sc_count; /* number of certificates */ + uint32_t sc_sizes_offset; /* offset to certificates sizes array */ + uint32_t sc_certs_offset; /* offset to certificates array */ +} kssl_certs_t; + + +#define SSL_RSA_WITH_NULL_SHA 0x0002 +#define SSL_RSA_WITH_RC4_128_MD5 0x0004 +#define SSL_RSA_WITH_RC4_128_SHA 0x0005 +#define SSL_RSA_WITH_DES_CBC_SHA 0x0009 +#define SSL_RSA_WITH_3DES_EDE_CBC_SHA 0x000a +#define CIPHER_SUITE_COUNT 5 +#define CIPHER_NOTSET 0xffff + +#define DEFAULT_SID_TIMEOUT 86400 /* 24 hours in seconds */ +#define DEFAULT_SID_CACHE_NENTRIES 5000 + +typedef struct kssl_params_s { + uint64_t kssl_params_size; /* total params buf len */ + /* address and port number */ + struct sockaddr_in kssl_addr; + uint16_t kssl_proxy_port; + + uint32_t kssl_session_cache_timeout; /* In seconds */ + uint32_t kssl_session_cache_size; + + /* + * Contains ordered list of cipher suites. We do not include + * the one suite with no encryption. Hence the -1. + */ + uint16_t kssl_suites[CIPHER_SUITE_COUNT - 1]; + + /* certificates */ + kssl_certs_t kssl_certs; + + /* private key */ + kssl_key_t kssl_privkey; +} kssl_params_t; + +/* The ioctls to /dev/kssl */ +#define KSSL_IOC(x) (('k' << 8) | (x)) +#define KSSL_ADD_ENTRY KSSL_IOC(1) +#define KSSL_DELETE_ENTRY KSSL_IOC(2) + +#ifdef _KERNEL + +extern int kssl_add_entry(kssl_params_t *); +extern int kssl_delete_entry(struct sockaddr_in *); + +#endif /* _KERNEL */ + +#ifdef __cplusplus +} +#endif + +#endif /* _INET_KSSL_KSSL_H */ diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/common/inet/kssl/ksslapi.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/inet/kssl/ksslapi.c Sat Nov 12 18:58:05 2005 -0800 @@ -0,0 +1,1185 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "ksslimpl.h" +#include "ksslproto.h" +#include "ksslapi.h" + +static kssl_cmd_t kssl_handle_any_record(kssl_ctx_t ctx, mblk_t *mp, + mblk_t **decrmp, kssl_callback_t cbfn, void *arg); +static boolean_t kssl_enqueue(kssl_chain_t **head, void *item); +static void kssl_dequeue(kssl_chain_t **head, void *item); +static kssl_status_t kssl_build_single_record(ssl_t *ssl, mblk_t *mp); + +/* + * The socket T_bind_req message is intercepted and re-routed here + * to see is there is SSL relevant job to do, based on the kssl config + * in the kssl_entry_tab. + * Looks up the kernel SSL proxy table, to find an entry that matches the + * same serveraddr, and has one of the following two criteria: + * 1. in_port is an ssl_port. This endpoint can be used later as a fallback + * to complete connections that cannot be handled by the SSL kernel proxy + * (typically non supported ciphersuite). The cookie for the calling client + * is saved with the kssl_entry to be retrieved for the fallback. + * The function returns KSSL_HAS_PROXY. + * + * 2. in_port is a proxy port for another ssl port. The ssl port is then + * substituted to the in_port in the bind_req TPI structure, so that + * the bind falls through to the SSL port. At the end of this operation, + * all the packets arriving to the SSL port will be delivered to an + * accepted endpoint child of this bound socket. + * The kssl_entry_t is returned in *ksslent, for later use by the + * lower modules' SSL hooks that handle the Handshake messages. + * The function returns KSSL_IS_PROXY. + * + * The function returns KSSL_NO_PROXY otherwise. We do not suppport + * IPv6 addresses. + */ + +kssl_endpt_type_t +kssl_check_proxy(mblk_t *bindmp, void *cookie, kssl_ent_t *ksslent) +{ + int i; + kssl_endpt_type_t ret; + kssl_entry_t *ep; + sin_t *sin; + struct T_bind_req *tbr; + ipaddr_t v4addr; + in_port_t in_port; + + if (kssl_enabled == 0) { + return (KSSL_NO_PROXY); + } + + tbr = (struct T_bind_req *)bindmp->b_rptr; + + ret = KSSL_NO_PROXY; + + + switch (tbr->ADDR_length) { + case sizeof (sin_t): + sin = (sin_t *)(bindmp->b_rptr + tbr->ADDR_length); + in_port = ntohs(sin->sin_port); + v4addr = sin->sin_addr.s_addr; + break; + + case sizeof (sin6_t): + /* Future support of IPv6 goes here */ + default: + /* Should ASSERT() here? */ + return (ret); + } + + mutex_enter(&kssl_tab_mutex); + + for (i = 0; i < kssl_entry_tab_size; i++) { + if ((ep = kssl_entry_tab[i]) == NULL) + continue; + + if ((ep->ke_laddr == v4addr) || (ep->ke_laddr == INADDR_ANY)) { + + /* This is an SSL port to fallback to */ + if (ep->ke_ssl_port == in_port) { + + /* + * Let's see first if there's at least + * an endpoint for a proxy server. + * If there's none, then we return as we have + * no proxy, so that the bind() to the + * transport layer goes through. + * The calling module will ask for this + * cookie if it wants to fall back to it, + * so add this one to the list of fallback + * clients. + */ + if (!kssl_enqueue((kssl_chain_t **) + &(ep->ke_fallback_head), cookie)) { + break; + } + + /* + * Now transform the T_BIND_REQ into + * a T_BIND_ACK. + */ + tbr->PRIM_type = T_BIND_ACK; + bindmp->b_datap->db_type = M_PCPROTO; + + KSSL_ENTRY_REFHOLD(ep); + *ksslent = (kssl_ent_t)ep; + + ret = KSSL_HAS_PROXY; + break; + } + + /* This is a proxy port. */ + if (ep->ke_proxy_port == in_port) { + mblk_t *entmp; + + /* Append this entry to the bind_req mblk */ + + entmp = allocb(sizeof (kssl_entry_t), + BPRI_MED); + if (entmp == NULL) + break; + *((kssl_entry_t **)entmp->b_rptr) = ep; + + entmp->b_wptr = entmp->b_rptr + + sizeof (kssl_entry_t); + + bindmp->b_cont = entmp; + + /* Add the caller's cookie to proxies list */ + + if (!kssl_enqueue((kssl_chain_t **) + &(ep->ke_proxy_head), cookie)) { + freeb(bindmp->b_cont); + bindmp->b_cont = NULL; + break; + } + + /* + * Make this look like the SSL port to the + * transport below + */ + sin->sin_port = htons(ep->ke_ssl_port); + + tbr->PRIM_type = T_SSL_PROXY_BIND_REQ; + + KSSL_ENTRY_REFHOLD(ep); + *ksslent = (kssl_ent_t)ep; + + ret = KSSL_IS_PROXY; + break; + } + } + } + + mutex_exit(&kssl_tab_mutex); + return (ret); +} + +/* + * Retrieved an endpoint "bound" to the SSL entry. + * Such endpoint has previously called kssl_check_proxy(), got itself + * linked to the kssl_entry's ke_fallback_head list. + * This routine returns the cookie from that SSL entry ke_fallback_head list. + */ +void * +kssl_find_fallback(kssl_ent_t ksslent) +{ + kssl_entry_t *kssl_entry = (kssl_entry_t *)ksslent; + + if (kssl_entry->ke_fallback_head != NULL) + return (kssl_entry->ke_fallback_head->fallback_bound); + + KSSL_COUNTER(proxy_fallback_failed, 1); + + return (NULL); +} + +/* + * Re-usable code for adding and removing an element to/from a chain that + * matches "item" + * The chain is simple-linked and NULL ended. + */ + +/* + * This routine returns TRUE if the item was either successfully added to + * the chain, or is already there. It returns FALSE otherwise. + */ +static boolean_t +kssl_enqueue(kssl_chain_t **head, void *item) +{ + kssl_chain_t *newchain, *cur; + + /* Lookup the existing entries to avoid duplicates */ + cur = *head; + while (cur != NULL) { + if (cur->item == item) { + return (B_TRUE); + } + cur = cur->next; + } + + newchain = kmem_alloc(sizeof (kssl_chain_t), KM_NOSLEEP); + if (newchain == NULL) { + return (B_FALSE); + } + + newchain->item = item; + newchain->next = *head; + *head = newchain; + return (B_TRUE); +} + +static void +kssl_dequeue(kssl_chain_t **head, void *item) +{ + kssl_chain_t *prev, *cur; + + prev = cur = *head; + while (cur != NULL) { + if (cur->item == item) { + if (cur == *head) + *head = (*head)->next; + else + prev->next = cur->next; + kmem_free(cur, sizeof (kssl_chain_t)); + return; + } + prev = cur; + cur = cur->next; + } +} + +/* + * Holds the kssl_entry + */ +void +kssl_hold_ent(kssl_ent_t ksslent) +{ + KSSL_ENTRY_REFHOLD((kssl_entry_t *)ksslent); +} + +/* + * Releases the kssl_entry + * If the caller passes a cookie, then it should be removed from both + * proxies and fallbacks chains. + */ +void +kssl_release_ent(kssl_ent_t ksslent, void *cookie, kssl_endpt_type_t endpt_type) +{ + kssl_entry_t *kssl_entry = (kssl_entry_t *)ksslent; + + if (cookie != NULL) { + if (endpt_type == KSSL_IS_PROXY) + ASSERT(kssl_entry->ke_proxy_head != NULL); + kssl_dequeue( + (kssl_chain_t **)&kssl_entry->ke_proxy_head, + cookie); + if (endpt_type == KSSL_HAS_PROXY) + ASSERT(kssl_entry->ke_fallback_head != NULL); + kssl_dequeue( + (kssl_chain_t **)&kssl_entry->ke_fallback_head, + cookie); + } + KSSL_ENTRY_REFRELE(kssl_entry); +} + +/* + * Holds the kssl context + */ +void +kssl_hold_ctx(kssl_ctx_t ksslctx) +{ + ssl_t *ssl = (ssl_t *)ksslctx; + + KSSL_SSL_REFHOLD(ssl); +} + +/* + * Releases the kssl_context + */ +void +kssl_release_ctx(kssl_ctx_t ksslctx) +{ + KSSL_SSL_REFRELE((ssl_t *)ksslctx); +} + +/* + * Packets are accumulated here, if there are packets already queued, + * or if the context is active. + * The context is active when an incoming record processing function + * is already executing on a different thread. + * Queued packets are handled either when an mblk arrived and completes + * a record, or, when the active context processor finishes the task at + * hand. + * The caller has to keep calling this routine in a loop until it returns + * B_FALSE in *more. The reason for this is SSL3: The protocol + * allows the client to send its first application_data message right + * after it had sent its Finished message, and not wait for the server + * ChangeCipherSpec and Finished. This overlap means we can't batch up + * a returned Handshake message to be sent on the wire + * with a decrypted application_data to be delivered to the application. + */ +kssl_cmd_t +kssl_input(kssl_ctx_t ctx, mblk_t *mp, mblk_t **decrmp, boolean_t *more, + kssl_callback_t cbfn, void *arg) +{ + mblk_t *recmp, *outmp = NULL; + kssl_cmd_t kssl_cmd; + ssl_t *ssl; + uint8_t *rec_sz_p; + int mplen; + SSL3ContentType content_type; + uint16_t rec_sz; + + ASSERT(ctx != NULL); + + if (mp != NULL) { + ASSERT(mp->b_prev == NULL && mp->b_next == NULL); + } + + ssl = (ssl_t *)(ctx); + + *decrmp = NULL; + *more = B_FALSE; + + mutex_enter(&ssl->kssl_lock); + + if (ssl->close_notify == B_TRUE) { + goto sendnewalert; + } + + /* Whomever is currently processing this connection will get to this */ + if (ssl->activeinput) { + if (mp != NULL) { + KSSL_ENQUEUE_MP(ssl, mp); + } + mutex_exit(&ssl->kssl_lock); + return (KSSL_CMD_NONE); + } + + /* + * Fast path for complete incoming application_data records on an empty + * queue. + * This is by far the most frequently encountered case + */ + + if ((!ssl->activeinput) && (ssl->rec_ass_head == NULL) && + ((mp != NULL) && (mplen = MBLKL(mp)) > SSL3_HDR_LEN)) { + + content_type = (SSL3ContentType)mp->b_rptr[0]; + + if ((content_type == content_application_data) && + (ssl->hs_waitstate == idle_handshake)) { + rec_sz_p = SSL3_REC_SIZE(mp); + rec_sz = BE16_TO_U16(rec_sz_p); + + if ((mp->b_cont == NULL) && (mplen == rec_sz)) { + + mp->b_flag &= ~DBLK_COOKED; + *decrmp = mp; + mutex_exit(&ssl->kssl_lock); + return (KSSL_CMD_DELIVER_PROXY); + } + } + } + + ssl->activeinput = B_TRUE; + /* Accumulate at least one record */ + if (mp != NULL) { + KSSL_ENQUEUE_MP(ssl, mp); + mp = NULL; + } + recmp = kssl_get_next_record(ssl); + + if (recmp == NULL) { + ssl->activeinput = B_FALSE; + if (ssl->alert_sendbuf != NULL) { + goto sendalert; + } + /* Not even a complete header yet. wait for the rest */ + mutex_exit(&ssl->kssl_lock); + return (KSSL_CMD_NONE); + } + + do { + if ((SSL3ContentType)recmp->b_rptr[0] == + content_application_data) { + /* + * application_data records are decrypted and + * MAC-verified by the stream head, and in the context + * a read()'ing thread. This avoids unfairly charging + * the cost of handling this record on the whole system, + * and prevents doing it while in the shared IP + * perimeter. + */ + ssl->activeinput = B_FALSE; + if (ssl->hs_waitstate != idle_handshake) { + goto sendnewalert; + } + outmp = recmp; + kssl_cmd = KSSL_CMD_DELIVER_PROXY; + } else { + /* + * If we're past the initial handshake, start letting + * the stream head process all records, in particular + * the close_notify. + * This is needed to avoid processing them out of + * sequence when previous application data packets are + * waiting to be decrypted/MAC'ed and delivered. + */ + if (ssl->hs_waitstate == idle_handshake) { + ssl->activeinput = B_FALSE; + outmp = recmp; + kssl_cmd = KSSL_CMD_DELIVER_PROXY; + } else { + kssl_cmd = kssl_handle_any_record(ssl, recmp, + &outmp, cbfn, arg); + } + } + + /* Priority to Alert messages */ + if (ssl->alert_sendbuf != NULL) { + goto sendalert; + } + + /* Then handshake messages */ + if (ssl->handshake_sendbuf) { + if (*decrmp != NULL) { + linkb(*decrmp, ssl->handshake_sendbuf); + } else { + *decrmp = ssl->handshake_sendbuf; + } + ssl->handshake_sendbuf = NULL; + + *more = ((ssl->rec_ass_head != NULL) && + (!ssl->activeinput)); + mutex_exit(&ssl->kssl_lock); + return (kssl_cmd); + } + + if (ssl->hs_waitstate == idle_handshake) { + *more = ((ssl->rec_ass_head != NULL) && + (!ssl->activeinput)); + } + + if (outmp != NULL) { + *decrmp = outmp; + /* + * Don't process any packet after an application_data. + * We could well receive the close_notify which should + * be handled separately. + */ + mutex_exit(&ssl->kssl_lock); + return (kssl_cmd); + } + /* + * The current record isn't done yet. Don't start the next one + */ + if (ssl->activeinput) { + mutex_exit(&ssl->kssl_lock); + return (kssl_cmd); + } + } while ((recmp = kssl_get_next_record(ssl)) != NULL); + + mutex_exit(&ssl->kssl_lock); + return (kssl_cmd); + +sendnewalert: + kssl_send_alert(ssl, alert_fatal, unexpected_message); + if (mp != NULL) { + freeb(mp); + } + +sendalert: + *decrmp = ssl->alert_sendbuf; + ssl->alert_sendbuf = NULL; + mutex_exit(&ssl->kssl_lock); + return (KSSL_CMD_SEND); +} + +/* + * Decrypt and verify the MAC of an incoming chain of application_data record. + * Each block has exactly one SSL record. + * This routine recycles its incoming mblk, and flags it as DBLK_COOKED + */ +kssl_cmd_t +kssl_handle_record(kssl_ctx_t ctx, mblk_t **mpp, mblk_t **outmp) +{ + uchar_t *recend, *rec_sz_p; + uchar_t *real_recend; + mblk_t *prevmp = NULL, *nextmp, *mp = *mpp, *copybp; + int mac_sz; + uchar_t version[2]; + uint16_t rec_sz; + SSL3AlertDescription desc; + SSL3ContentType content_type; + ssl_t *ssl; + KSSLCipherSpec *spec; + int error = 0, ret; + kssl_cmd_t kssl_cmd = KSSL_CMD_DELIVER_PROXY; + boolean_t deliverit = B_FALSE; + crypto_data_t cipher_data; + + ASSERT(ctx != NULL); + + ssl = (ssl_t *)(ctx); + + *outmp = NULL; + +more: + + while (mp != NULL) { + + if (DB_REF(mp) > 1) { + /* + * Fortunately copyb() preserves the offset, + * tail space and alignement so the copy is + * ready to be made an SSL record. + */ + if ((copybp = copyb(mp)) == NULL) + return (NULL); + + copybp->b_cont = mp->b_cont; + if (mp == *mpp) { + *mpp = copybp; + } else { + prevmp->b_cont = copybp; + } + freeb(mp); + mp = copybp; + } + + content_type = (SSL3ContentType)mp->b_rptr[0]; + + if (content_type != content_application_data) { + nextmp = mp->b_cont; + + /* Remove this message */ + if (prevmp != NULL) { + prevmp->b_cont = nextmp; + + /* + * If we had processed blocks that need to + * be delivered, then remember that error code + */ + if (kssl_cmd == KSSL_CMD_DELIVER_PROXY) + deliverit = B_TRUE; + } + + mutex_enter(&ssl->kssl_lock); + kssl_cmd = kssl_handle_any_record(ssl, mp, outmp, + NULL, NULL); + + if (ssl->alert_sendbuf != NULL) { + goto sendalert; + } + mutex_exit(&ssl->kssl_lock); + + if (deliverit) { + kssl_cmd = KSSL_CMD_DELIVER_PROXY; + } + + mp = nextmp; + continue; + } + + version[0] = mp->b_rptr[1]; + version[1] = mp->b_rptr[2]; + rec_sz_p = SSL3_REC_SIZE(mp); + rec_sz = BE16_TO_U16(rec_sz_p); + + mp->b_rptr += SSL3_HDR_LEN; + recend = mp->b_rptr + rec_sz; + real_recend = recend; + + spec = &ssl->spec[KSSL_READ]; + mac_sz = spec->mac_hashsz; + if (spec->cipher_ctx != 0) { + cipher_data.cd_format = CRYPTO_DATA_RAW; + cipher_data.cd_offset = 0; + cipher_data.cd_length = rec_sz; + cipher_data.cd_miscdata = NULL; + cipher_data.cd_raw.iov_base = (char *)mp->b_rptr; + cipher_data.cd_raw.iov_len = rec_sz; + error = crypto_decrypt_update(spec->cipher_ctx, + &cipher_data, NULL, NULL); + if (CRYPTO_ERR(error)) { +#ifdef DEBUG + cmn_err(CE_WARN, "kssl_handle_record: " + "crypto_decrypt_update failed: 0x%02X", + error); +#endif /* DEBUG */ + KSSL_COUNTER(record_decrypt_failure, 1); + mp->b_rptr = recend; + desc = decrypt_error; + goto makealert; + } + } + if (spec->cipher_type == type_block) { + uint_t pad_sz = recend[-1]; + pad_sz++; + if (pad_sz + mac_sz > rec_sz) { + mp->b_rptr = recend; + desc = bad_record_mac; + goto makealert; + } + rec_sz -= pad_sz; + recend -= pad_sz; + } + if (mac_sz != 0) { + uchar_t hash[MAX_HASH_LEN]; + if (rec_sz < mac_sz) { + mp->b_rptr = real_recend; + desc = bad_record_mac; + goto makealert; + } + rec_sz -= mac_sz; + recend -= mac_sz; + ret = kssl_compute_record_mac(ssl, KSSL_READ, + ssl->seq_num[KSSL_READ], content_type, + version, mp->b_rptr, rec_sz, hash); + if (ret != CRYPTO_SUCCESS || + bcmp(hash, recend, mac_sz)) { + mp->b_rptr = real_recend; + desc = bad_record_mac; +#ifdef DEBUG + cmn_err(CE_WARN, "kssl_handle_record: " + "msg MAC mismatch"); +#endif /* DEBUG */ + KSSL_COUNTER(verify_mac_failure, 1); + goto makealert; + } + ssl->seq_num[KSSL_READ]++; + } + + if (ssl->hs_waitstate != idle_handshake) { + mp->b_rptr = real_recend; + desc = unexpected_message; + goto makealert; + } + mp->b_wptr = recend; + + prevmp = mp; + mp = mp->b_cont; + } + + KSSL_COUNTER(appdata_record_ins, 1); + return (kssl_cmd); + +makealert: + nextmp = mp->b_cont; + freeb(mp); + mp = nextmp; + mutex_enter(&ssl->kssl_lock); + kssl_send_alert(ssl, alert_fatal, desc); + + if (ssl->alert_sendbuf == NULL) { + /* internal memory allocation failure. just return. */ +#ifdef DEBUG + cmn_err(CE_WARN, "kssl_handle_record: " + "alert message allocation failed"); +#endif /* DEBUG */ + mutex_exit(&ssl->kssl_lock); + + if (mp) { + prevmp = NULL; + goto more; + } + + return (KSSL_CMD_NONE); + } + kssl_cmd = KSSL_CMD_SEND; +sendalert: + if (*outmp == NULL) { + *outmp = ssl->alert_sendbuf; + } else { + linkb(*outmp, ssl->alert_sendbuf); + } + ssl->alert_sendbuf = NULL; + mutex_exit(&ssl->kssl_lock); + + if (mp) { + prevmp = NULL; + goto more; + } + + return (kssl_cmd); +} +/* + * This is the routine that handles incoming SSL records. + * When called the first time, with a NULL context, this routine expects + * a ClientHello SSL Handshake packet and shall allocate a context + * of a new SSL connection. + * During the rest of the handshake packets, the routine adjusts the + * state of the context according to the record received. + * After the ChangeCipherSpec message is received, the routine first + * decrypts/authenticated the packet using the key materials in the + * connection's context. + * The return code tells the caller what to do with the returned packet. + */ +static kssl_cmd_t +kssl_handle_any_record(kssl_ctx_t ctx, mblk_t *mp, mblk_t **decrmp, + kssl_callback_t cbfn, void *arg) +{ + uchar_t *recend, *rec_sz_p; + uchar_t version[2]; + uchar_t *real_recend, *save_rptr, *save_wptr; + int rhsz = SSL3_HDR_LEN; + uint16_t rec_sz; + int sz; + int mac_sz; + SSL3AlertDescription desc; + SSL3AlertLevel level; + SSL3ContentType content_type; + ssl_t *ssl; + KSSLCipherSpec *spec; + int error = 0, ret; + + ASSERT(ctx != NULL); + + ssl = (ssl_t *)(ctx); + + *decrmp = NULL; + + save_rptr = mp->b_rptr; + save_wptr = mp->b_wptr; + + ASSERT(MUTEX_HELD(&ssl->kssl_lock)); + + content_type = (SSL3ContentType)mp->b_rptr[0]; + if (content_type == content_handshake_v2) { + if (ssl->hs_waitstate == wait_client_hello) { + /* V2 compatible ClientHello */ + if (mp->b_rptr[3] == 0x03 && + (mp->b_rptr[4] == 0x01 || + mp->b_rptr[4] == 0x00)) { + ssl->major_version = version[0] = mp->b_rptr[3]; + ssl->minor_version = version[1] = mp->b_rptr[4]; + } else { + /* We don't support "pure" SSLv2 */ + desc = protocol_version; + goto sendalert; + } + } + rec_sz = (uint16_t)mp->b_rptr[1]; + rhsz = 2; + } else { + ssl->major_version = version[0] = mp->b_rptr[1]; + ssl->minor_version = version[1] = mp->b_rptr[2]; + rec_sz_p = SSL3_REC_SIZE(mp); + rec_sz = BE16_TO_U16(rec_sz_p); + } + + mp->b_rptr += rhsz; + recend = mp->b_rptr + rec_sz; + real_recend = recend; + + spec = &ssl->spec[KSSL_READ]; + mac_sz = spec->mac_hashsz; + if (spec->cipher_ctx != 0) { + spec->cipher_data.cd_length = rec_sz; + spec->cipher_data.cd_raw.iov_base = (char *)mp->b_rptr; + spec->cipher_data.cd_raw.iov_len = rec_sz; + error = crypto_decrypt_update(spec->cipher_ctx, + &spec->cipher_data, NULL, NULL); + if (CRYPTO_ERR(error)) { +#ifdef DEBUG + cmn_err(CE_WARN, + "kssl_handle_any_record: crypto_decrypt_update " + "failed: 0x%02X", error); +#endif /* DEBUG */ + KSSL_COUNTER(record_decrypt_failure, 1); + mp->b_rptr = recend; + desc = decrypt_error; + goto sendalert; + } + } + if (spec->cipher_type == type_block) { + uint_t pad_sz = recend[-1]; + pad_sz++; + if (pad_sz + mac_sz > rec_sz) { + mp->b_rptr = recend; + desc = bad_record_mac; + goto sendalert; + } + rec_sz -= pad_sz; + recend -= pad_sz; + } + if (mac_sz != 0) { + uchar_t hash[MAX_HASH_LEN]; + if (rec_sz < mac_sz) { + mp->b_rptr = real_recend; + desc = bad_record_mac; + goto sendalert; + } + rec_sz -= mac_sz; + recend -= mac_sz; + ret = kssl_compute_record_mac(ssl, KSSL_READ, + ssl->seq_num[KSSL_READ], content_type, + version, mp->b_rptr, rec_sz, hash); + if (ret != CRYPTO_SUCCESS || + bcmp(hash, recend, mac_sz)) { + mp->b_rptr = real_recend; + desc = bad_record_mac; +#ifdef DEBUG + cmn_err(CE_WARN, "kssl_handle_any_record: " + "msg MAC mismatch"); +#endif /* DEBUG */ + KSSL_COUNTER(verify_mac_failure, 1); + goto sendalert; + } + ssl->seq_num[KSSL_READ]++; + } + + switch (content_type) { + case content_handshake: + do { + if (error != 0 || + /* ignore client renegotiation for now */ + ssl->hs_waitstate == idle_handshake) { + mp->b_rptr = recend; + } + if (mp->b_rptr == recend) { + mp->b_rptr = real_recend; + if (error != 0) { + goto error; + } + freeb(mp); + + if (ssl->hs_waitstate == wait_client_key_done) + return (KSSL_CMD_QUEUED); + + return ((ssl->handshake_sendbuf != NULL) ? + KSSL_CMD_SEND : KSSL_CMD_NONE); + } + if (ssl->msg.state < MSG_BODY) { + if (ssl->msg.state == MSG_INIT) { + ssl->msg.type = + (SSL3HandshakeType)*mp->b_rptr++; + ssl->msg.state = MSG_INIT_LEN; + } + if (ssl->msg.state == MSG_INIT_LEN) { + int msglenb = + ssl->msg.msglen_bytes; + int msglen = ssl->msg.msglen; + while (mp->b_rptr < recend && + msglenb < 3) { + msglen = (msglen << 8) + + (uint_t)(*mp->b_rptr++); + msglenb++; + } + ssl->msg.msglen_bytes = msglenb; + ssl->msg.msglen = msglen; + if (msglenb == 3) { + ssl->msg.state = MSG_BODY; + } + } + if (mp->b_rptr == recend) { + mp->b_rptr = real_recend; + freeb(mp); + return (KSSL_CMD_NONE); + } + } + ASSERT(ssl->msg.state == MSG_BODY); + + sz = recend - mp->b_rptr; + + if (ssl->msg.head == NULL && + ssl->msg.msglen <= sz) { + continue; + } + if (ssl->msg.head != NULL) { + sz += msgdsize(ssl->msg.head); + if (ssl->msg.msglen <= sz) { + ssl->msg.tail->b_cont = mp; + mp = ssl->msg.head; + ssl->sslcnt = 100; + ssl->msg.head = NULL; + ssl->msg.tail = NULL; + if (pullupmsg(mp, -1)) { + recend = mp->b_rptr + sz; + ASSERT(recend <= mp->b_wptr); + continue; + } + mp->b_rptr = real_recend; + error = ENOMEM; + KSSL_COUNTER(alloc_fails, 1); + goto error; + } + } + + mp->b_wptr = recend; + + if (ssl->msg.head == NULL) { + ssl->msg.head = mp; + ssl->msg.tail = mp; + return (KSSL_CMD_NONE); + } else { + ssl->msg.tail->b_cont = mp; + ssl->msg.tail = mp; + return (KSSL_CMD_NONE); + } + } while (kssl_handle_handshake_message(ssl, mp, &error, cbfn, + arg)); + if (error == SSL_MISS) { + mp->b_rptr = save_rptr; + mp->b_wptr = save_wptr; + KSSL_COUNTER(fallback_connections, 1); + return (KSSL_CMD_NOT_SUPPORTED); + } + if (ssl->hs_waitstate == wait_client_key_done) { + return (KSSL_CMD_QUEUED); + } else { + return (KSSL_CMD_NONE); + } + case content_alert: + if (rec_sz != 2) { + mp->b_rptr = real_recend; + desc = illegal_parameter; + goto sendalert; + } else { + level = *mp->b_rptr++; + desc = *mp->b_rptr++; + mp->b_rptr = real_recend; + if (level != alert_warning || desc != close_notify) { + if (ssl->sid.cached == B_TRUE) { + kssl_uncache_sid(&ssl->sid, + ssl->kssl_entry); + ssl->sid.cached = B_FALSE; + } + ssl->fatal_alert = B_TRUE; + error = EBADMSG; + goto error; + } else { + ssl->close_notify = B_TRUE; + ssl->activeinput = B_FALSE; + freeb(mp); + return (KSSL_CMD_NONE); + } + } + case content_change_cipher_spec: + if (ssl->hs_waitstate != wait_change_cipher) { + desc = unexpected_message; + } else if (rec_sz != 1 || *mp->b_rptr != 1) { + desc = illegal_parameter; + } else { + mp->b_rptr = real_recend; + ssl->hs_waitstate = wait_finished; + ssl->seq_num[KSSL_READ] = 0; + if ((error = kssl_spec_init(ssl, KSSL_READ)) != 0) { +#ifdef DEBUG + cmn_err(CE_WARN, + "kssl_spec_init returned error " + "0x%02X", error); +#endif /* DEBUG */ + goto error; + } + ssl->activeinput = B_FALSE; + freeb(mp); + return (KSSL_CMD_NONE); + } + mp->b_rptr = real_recend; + goto sendalert; + + case content_application_data: + if (ssl->hs_waitstate != idle_handshake) { + mp->b_rptr = real_recend; + desc = unexpected_message; + goto sendalert; + } + mp->b_wptr = recend; + *decrmp = mp; + ssl->activeinput = B_FALSE; + return (KSSL_CMD_DELIVER_PROXY); + + case content_handshake_v2: + error = kssl_handle_v2client_hello(ssl, mp, rec_sz); + if (error == SSL_MISS) { + mp->b_rptr = save_rptr; + mp->b_wptr = save_wptr; + KSSL_COUNTER(fallback_connections, 1); + return (KSSL_CMD_NOT_SUPPORTED); + } else if (error != 0) { + goto error; + } + freeb(mp); + return (KSSL_CMD_SEND); + default: + mp->b_rptr = real_recend; + desc = unexpected_message; + break; + } + +sendalert: + kssl_send_alert(ssl, alert_fatal, desc); + *decrmp = ssl->alert_sendbuf; + ssl->alert_sendbuf = NULL; + freeb(mp); + return ((*decrmp != NULL) ? KSSL_CMD_SEND : KSSL_CMD_NONE); +error: + freeb(mp); + return (KSSL_CMD_NONE); +} + +/* + * Initialize the context of an SSL connection, coming to the specified + * address. + * the ssl structure returned is held. + */ +kssl_status_t +kssl_init_context(kssl_ent_t kssl_ent, ipaddr_t faddr, int mss, + kssl_ctx_t *kssl_ctxp) +{ + ssl_t *ssl = kmem_cache_alloc(kssl_cache, KM_NOSLEEP); + + if (ssl == NULL) { + return (KSSL_STS_ERR); + } + + kssl_cache_count++; + + bzero(ssl, sizeof (ssl_t)); + + ssl->kssl_entry = (kssl_entry_t *)kssl_ent; + KSSL_ENTRY_REFHOLD(ssl->kssl_entry); + + ssl->faddr = faddr; + ssl->tcp_mss = mss; + ssl->sendalert_level = alert_warning; + ssl->sendalert_desc = close_notify; + ssl->sid.cached = B_FALSE; + + *kssl_ctxp = (kssl_ctx_t)ssl; + KSSL_SSL_REFHOLD(ssl); + return (KSSL_STS_OK); +} + +/* + * Builds SSL records out of the chain of mblks, and returns it. + * Taked a copy of the message before encypting it if it has another + * reference. + * In case of failure, NULL is returned, and the message will be + * freed by the caller. + * A NULL mp means a close_notify is requested. + */ +mblk_t * +kssl_build_record(kssl_ctx_t ctx, mblk_t *mp) +{ + ssl_t *ssl = (ssl_t *)ctx; + mblk_t *retmp = mp, *bp = mp, *prevbp = mp, *copybp; + + ASSERT(ssl != NULL); + ASSERT(mp != NULL); + + do { + if (DB_REF(bp) > 1) { + /* + * Fortunately copyb() preserves the offset, + * tail space and alignement so the copy is + * ready to be made an SSL record. + */ + if ((copybp = copyb(bp)) == NULL) + return (NULL); + + copybp->b_cont = bp->b_cont; + if (bp == mp) { + retmp = copybp; + } else { + prevbp->b_cont = copybp; + } + freeb(bp); + bp = copybp; + } + + if (kssl_build_single_record(ssl, bp) != KSSL_STS_OK) + return (NULL); + + prevbp = bp; + bp = bp->b_cont; + } while (bp != NULL); + + return (retmp); +} + +/* + * Builds a single SSL record + * In-line encryption of the record. + */ +static kssl_status_t +kssl_build_single_record(ssl_t *ssl, mblk_t *mp) +{ + int len; + int reclen = 0; + uchar_t *recstart, *versionp; + KSSLCipherSpec *spec; + int mac_sz; + int pad_sz = 0; + + + spec = &ssl->spec[KSSL_WRITE]; + mac_sz = spec->mac_hashsz; + + + ASSERT(DB_REF(mp) == 1); + ASSERT((mp->b_rptr - mp->b_datap->db_base >= SSL3_HDR_LEN) && + (mp->b_datap->db_lim - mp->b_wptr >= mac_sz + spec->cipher_bsize)); + + len = MBLKL(mp); + + ASSERT(len > 0); + + mutex_enter(&ssl->kssl_lock); + + recstart = mp->b_rptr = mp->b_rptr - SSL3_HDR_LEN; + recstart[0] = content_application_data; + recstart[1] = ssl->major_version; + recstart[2] = ssl->minor_version; + versionp = &recstart[1]; + + reclen = len + mac_sz; + if (spec->cipher_type == type_block) { + pad_sz = spec->cipher_bsize - + (reclen & (spec->cipher_bsize - 1)); + ASSERT(reclen + pad_sz <= + SSL3_MAX_RECORD_LENGTH); + reclen += pad_sz; + } + recstart[3] = (reclen >> 8) & 0xff; + recstart[4] = reclen & 0xff; + + if (kssl_mac_encrypt_record(ssl, content_application_data, versionp, + recstart, mp) != 0) { + /* Do we need an internal_error Alert here? */ + mutex_exit(&ssl->kssl_lock); + return (KSSL_STS_ERR); + } + + KSSL_COUNTER(appdata_record_outs, 1); + mutex_exit(&ssl->kssl_lock); + return (KSSL_STS_OK); +} diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/common/inet/kssl/ksslapi.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/inet/kssl/ksslapi.h Sat Nov 12 18:58:05 2005 -0800 @@ -0,0 +1,109 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _INET_KSSL_KSSLAPI_H +#define _INET_KSSL_KSSLAPI_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * The kernel SSL proxy interface + */ + + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/* return status for the kssl API functions */ + +typedef enum { + KSSL_STS_OK, /* No further processing required */ + KSSL_STS_ERR /* bogus argument ... */ +} kssl_status_t; + +/* Endpoint type */ +typedef enum { + KSSL_NO_PROXY = 0, /* Not configured for use with KSSL */ + KSSL_IS_PROXY, /* Acts as a proxy for someone else */ + KSSL_HAS_PROXY /* A proxy is handling its work */ +} kssl_endpt_type_t; + +/* Return codes/commands from kssl_handle_record */ +typedef enum { + KSSL_CMD_NOT_SUPPORTED, /* Not supported */ + KSSL_CMD_SEND, /* send this packet out on the wire */ + KSSL_CMD_DELIVER_PROXY, /* deliver this packet to proxy listener */ + KSSL_CMD_DELIVER_SSL, /* Deliver to the SSL listener */ + KSSL_CMD_NONE, /* consider it consumed. (ACK it, ... */ + KSSL_CMD_QUEUED /* Queued, a call back will finish it */ +} kssl_cmd_t; + +typedef enum { + KSSL_EVENT_CLOSE /* close this context */ +} kssl_event_t; + +/* Un opaque context of an SSL connection */ +typedef void *kssl_ctx_t; + +/* Un opaque handle for an SSL map entry */ +typedef void *kssl_ent_t; + +#define SSL3_HDR_LEN 5 +#define SSL3_WROFFSET 7 /* 5 hdr + 2 byte-alignment */ +#define SSL3_MAX_TAIL_LEN 36 /* 16 AES blocks + 20 SHA1 digest */ +#define SSL3_MAX_RECORD_LEN 16384 - 1 - SSL3_HDR_LEN - SSL3_MAX_TAIL_LEN + + +kssl_endpt_type_t kssl_check_proxy(mblk_t *, void *, kssl_ent_t *); + +kssl_status_t kssl_init_context(kssl_ent_t, uint32_t, int, kssl_ctx_t *); + +void kssl_hold_ent(kssl_ent_t); +void kssl_release_ent(kssl_ent_t, void *, kssl_endpt_type_t); +void *kssl_find_fallback(kssl_ent_t); + +void kssl_hold_ctx(kssl_ctx_t); +void kssl_release_ctx(kssl_ctx_t); + +typedef void (*kssl_callback_t)(void *arg, mblk_t *mp, kssl_cmd_t cmd); + +kssl_cmd_t kssl_input(kssl_ctx_t, mblk_t *, mblk_t **, boolean_t *, + kssl_callback_t cbfn, void *arg); + +kssl_cmd_t kssl_handle_record(kssl_ctx_t, mblk_t **, mblk_t **); + +mblk_t *kssl_build_record(kssl_ctx_t, mblk_t *); + + +#ifdef __cplusplus +} +#endif + +#endif /* _INET_KSSL_KSSLAPI_H */ diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/common/inet/kssl/kssldebug.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/inet/kssl/kssldebug.h Sat Nov 12 18:58:05 2005 -0800 @@ -0,0 +1,111 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _INET_KSSL_KSSLDEBUG_H +#define _INET_KSSL_KSSLDEBUG_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +/* BEGIN CSTYLED */ +#ifdef DEBUG +extern int kssl_debug; + +#define KSSL_DEBUG1(a1) cmn_err(CE_CONT, (a1)) +#define KSSL_DEBUG2(a1,a2) cmn_err(CE_CONT, (a1),(a2)) +#define KSSL_DEBUG3(a1,a2,a3) cmn_err(CE_CONT, (a1),(a2),(a3)) +#define KSSL_DEBUG4(a1,a2,a3,a4) cmn_err(CE_CONT, (a1),(a2),(a3),(a4)) +#define KSSL_DEBUG5(a1,a2,a3,a4,a5) \ + cmn_err(CE_CONT,(a1),(a2),(a3),(a4),(a5)) +#define KSSL_DEBUG6(a1,a2,a3,a4,a5,a6) \ + cmn_err(CE_CONT,(a1),(a2),(a3),(a4),(a5),(a6)) +#define KSSL_DEBUG7(a1,a2,a3,a4,a5,a6,a7) \ + cmn_err(CE_CONT,(a1),(a2),(a3),(a4),(a5),(a6),(a7)) +#define KSSL_DEBUG8(a1,a2,a3,a4,a5,a6,a7,a8) \ + cmn_err(CE_CONT,(a1),(a2),(a3),(a4),(a5),(a6),(a7),(a8)) +#define KSSL_DEBUG9(a1,a2,a3,a4,a5,a6,a7,a8,a9) \ + cmn_err(CE_CONT,(a1),(a2),(a3),(a4),(a5),(a6),(a7),(a8),(a9)) + +#define KSSL_DEBUG12(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12) \ + cmn_err(CE_CONT,(a1),(a2),(a3),(a4),(a5),(a6),(a7),(a8),(a9),(a10),(a11), \ + (a12)) +#define KSSL_DEBUG14(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14) \ + cmn_err(CE_CONT,(a1),(a2),(a3),(a4),(a5),(a6),(a7),(a8),(a9),(a10),(a11), \ + (a12),(a13),(a14)) +#define KSSL_DEBUG15(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15) \ + cmn_err(CE_CONT,(a1),(a2),(a3),(a4),(a5),(a6),(a7),(a8),(a9),(a10),(a11), \ + (a12),(a13),(a14),(a15)) + +#define KSSL_DEBUG1_IF(c,a1) \ + { if (c) KSSL_DEBUG1((a1)); } +#define KSSL_DEBUG2_IF(c,a1,a2) \ + { if (c) KSSL_DEBUG2((a1),(a2)); } +#define KSSL_DEBUG3_IF(c,a1,a2,a3) \ + { if (c) KSSL_DEBUG3((a1),(a2),(a3)); } +#define KSSL_DEBUG4_IF(c,a1,a2,a3,a4) \ + { if (c) KSSL_DEBUG4((a1),(a2),(a3),(a4)); } +#define KSSL_DEBUG5_IF(c,a1,a2,a3,a4,a5) \ + { if (c) KSSL_DEBUG5((a1),(a2),(a3),(a4),(a5)); } +#define KSSL_DEBUG6_IF(c,a1,a2,a3,a4,a5,a6) \ + { if (c) KSSL_DEBUG6((a1),(a2),(a3),(a4),(a5),(a6)); } +#define KSSL_DEBUG7_IF(c,a1,a2,a3,a4,a5,a6,a7) \ + { if (c) KSSL_DEBUG7((a1),(a2),(a3),(a4),(a5),(a6),(a7)); } + +#else /* !DEBUG */ + +#define KSSL_DEBUG1(a1) /* empty */ +#define KSSL_DEBUG2(a1,a2) /* empty */ +#define KSSL_DEBUG3(a1,a2,a3) /* empty */ +#define KSSL_DEBUG4(a1,a2,a3,a4) /* empty */ +#define KSSL_DEBUG5(a1,a2,a3,a4,a5) /* empty */ +#define KSSL_DEBUG6(a1,a2,a3,a4,a5,a6) /* empty */ +#define KSSL_DEBUG7(a1,a2,a3,a4,a5,a6,a7) /* empty */ +#define KSSL_DEBUG8(a1,a2,a3,a4,a5,a6,a7,a8) /* empty */ +#define KSSL_DEBUG9(a1,a2,a3,a4,a5,a6,a7,a8,a9) /* empty */ + +#define KSSL_DEBUG12(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12) /* empty */ +#define KSSL_DEBUG14(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14) +#define KSSL_DEBUG15(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15) + +#define KSSL_DEBUG1_IF(c,a1) /* empty */ +#define KSSL_DEBUG2_IF(c,a1,a2) /* empty */ +#define KSSL_DEBUG3_IF(c,a1,a2,a3) /* empty */ +#define KSSL_DEBUG4_IF(c,a1,a2,a3,a4) /* empty */ +#define KSSL_DEBUG5_IF(c,a1,a2,a3,a4,a5) /* empty */ +#define KSSL_DEBUG6_IF(c,a1,a2,a3,a4,a5,a6) /* empty */ +#define KSSL_DEBUG7_IF(c,a1,a2,a3,a4,a5,a6,a7) /* empty */ + +#endif /* DEBUG */ +/* END CSTYLED */ + +#ifdef __cplusplus +} +#endif + +#endif /* _INET_KSSL_KSSLDEBUG_H */ diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/common/inet/kssl/ksslimpl.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/inet/kssl/ksslimpl.h Sat Nov 12 18:58:05 2005 -0800 @@ -0,0 +1,208 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _INET_KSSL_KSSLIMPL_H +#define _INET_KSSL_KSSLIMPL_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Certificate structure. The msg field is the BER data of the + * certificate. + */ +typedef struct Certificate { + uchar_t *msg; + int len; +} Certificate_t; + +/* Generic linked chain type */ +typedef struct kssl_chain_s { + struct kssl_chain_s *next; + void *item; +} kssl_chain_t; + +/* Proxies chain. follows the generic kssl_chain_t layout */ +typedef struct kssl_proxy_s { + struct kssl_proxy_s *next; + void *proxy_bound; +} kssl_proxy_t; + +/* Fallback endpoints chain. Ditto. */ +typedef struct kssl_fallback_s { + struct kssl_fallback_s *next; + void *fallback_bound; +} kssl_fallback_t; + +/* kssl_entry_t structure. */ + +typedef struct kssl_entry_s { + uint_t ke_refcnt; /* for hold/release */ + boolean_t ke_no_freeall; + kmutex_t ke_mutex; + + ipaddr_t ke_laddr; /* Only IPv4 is supported */ + in_port_t ke_ssl_port; /* SSL port */ + in_port_t ke_proxy_port; /* SSL proxy port */ + + uint32_t sid_cache_timeout; /* In seconds */ + uint32_t sid_cache_nentries; + kssl_sid_ent_t *sid_cache; + + uint16_t kssl_cipherSuites[CIPHER_SUITE_COUNT]; + int kssl_cipherSuites_nentries; + uint16_t kssl_saved_Suites[CIPHER_SUITE_COUNT]; + + crypto_key_t *ke_private_key; /* instance's private key */ + Certificate_t *ke_server_certificate; + + Certificate_t **ke_cacert_chain; + + kssl_proxy_t *ke_proxy_head; /* Proxies chain */ + kssl_fallback_t *ke_fallback_head; /* Fall-back endpoints chain */ + +} kssl_entry_t; + +typedef struct mech_to_cipher_s { + crypto_mech_type_t mech; + char *name; + uint16_t kssl_suites[CIPHER_SUITE_COUNT]; +} mech_to_cipher_t; + +#define KSSL_ENTRY_REFHOLD(kssl_entry) { \ + atomic_add_32(&(kssl_entry)->ke_refcnt, 1); \ + ASSERT((kssl_entry)->ke_refcnt != 0); \ +} + +#define KSSL_ENTRY_REFRELE(kssl_entry) { \ + ASSERT((kssl_entry)->ke_refcnt != 0); \ + membar_exit(); \ + if (atomic_add_32_nv(&(kssl_entry)->ke_refcnt, -1) == 0) { \ + kssl_free_entry((kssl_entry)); \ + } \ +} + +#define KSSL_SSL_REFHOLD(ssl) { \ + atomic_add_32(&(ssl)->kssl_refcnt, 1); \ + ASSERT((ssl)->kssl_refcnt != 0); \ + ASSERT((ssl)->kssl_refcnt < 100000); \ +} + +#define KSSL_SSL_REFRELE(ssl) { \ + ASSERT((ssl)->kssl_refcnt != 0); \ + ASSERT((ssl)->kssl_refcnt < 100000); \ + membar_exit(); \ + if (atomic_add_32_nv(&(ssl)->kssl_refcnt, -1) == 0) { \ + kssl_free_context((ssl)); \ + } \ +} + +#define CRYPTO_ERR(r) ((r) != CRYPTO_SUCCESS && (r) != CRYPTO_QUEUED) + +#define KSSL_ENQUEUE_MP(ssl, mp) \ + if ((ssl)->rec_ass_tail == NULL) { \ + (ssl)->rec_ass_head = (mp); \ + (ssl)->rec_ass_tail = (mp); \ + } else { \ + (ssl)->rec_ass_tail->b_cont = (mp); \ + (ssl)->rec_ass_tail = (mp); \ + } + +#define SSL_MISS 123 /* Internal SSL error */ + +extern crypto_mechanism_t rsa_x509_mech; +extern crypto_mechanism_t hmac_md5_mech; +extern crypto_mechanism_t hmac_sha1_mech; +extern crypto_call_flag_t kssl_call_flag; +extern KSSLCipherDef cipher_defs[]; + +extern int kssl_enabled; +extern int kssl_cache_count; +extern struct kmem_cache *kssl_cache; + +#define KSSL_TAB_INITSIZE 32 +extern kssl_entry_t **kssl_entry_tab; +extern int kssl_entry_tab_size; +extern int kssl_entry_tab_nentries; +extern kmutex_t kssl_tab_mutex; + +typedef struct kssl_stats { + kstat_named_t sid_cache_lookups; + kstat_named_t sid_cache_hits; + kstat_named_t sid_uncached; + kstat_named_t full_handshakes; + kstat_named_t resumed_sessions; + kstat_named_t fallback_connections; + kstat_named_t proxy_fallback_failed; + kstat_named_t appdata_record_ins; + kstat_named_t appdata_record_outs; + kstat_named_t alloc_fails; + kstat_named_t fatal_alerts; + kstat_named_t warning_alerts; + kstat_named_t no_suite_found; + kstat_named_t compute_mac_failure; + kstat_named_t verify_mac_failure; + kstat_named_t record_decrypt_failure; + kstat_named_t bad_pre_master_secret; +} kssl_stats_t; + +extern kssl_stats_t *kssl_statp; + +#define KSSL_COUNTER(p, v) atomic_add_64(&kssl_statp->p.value.ui64, v) + +#define IS_SSL_PORT 1 +#define IS_PROXY_PORT 2 + +extern void kssl_free_entry(kssl_entry_t *); +extern void kssl_free_context(ssl_t *); +extern int kssl_compute_record_mac(ssl_t *, int, uint64_t, SSL3ContentType, + uchar_t *, uchar_t *, int, uchar_t *); +extern int kssl_handle_handshake_message(ssl_t *, mblk_t *, int *, + kssl_callback_t, void *); +extern int kssl_handle_v2client_hello(ssl_t *, mblk_t *, int); +extern void kssl_uncache_sid(sslSessionID *, kssl_entry_t *); +extern int kssl_mac_encrypt_record(ssl_t *, SSL3ContentType, uchar_t *, + uchar_t *, mblk_t *); +extern mblk_t *kssl_get_next_record(ssl_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* _INET_KSSL_KSSLIMPL_H */ diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/common/inet/kssl/ksslioctl.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/inet/kssl/ksslioctl.c Sat Nov 12 18:58:05 2005 -0800 @@ -0,0 +1,599 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * The kernel SSL module ioctls. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ksslimpl.h" +#include "kssl.h" +#include "ksslproto.h" + +kssl_entry_t **kssl_entry_tab; +int kssl_entry_tab_size; +int kssl_entry_tab_nentries; +kmutex_t kssl_tab_mutex; + + +static void +certificate_free(Certificate_t *cert) +{ + kmem_free(cert->msg, cert->len); + kmem_free(cert, sizeof (struct Certificate)); +} + +static void +privateKey_free(crypto_key_t *privkey) +{ + crypto_object_attribute_t *attrs = privkey->ck_attrs; + size_t attrs_size + = privkey->ck_count * sizeof (crypto_object_attribute_t); + + int i; + + for (i = 0; i < privkey->ck_count; i++) { + bzero(attrs[i].oa_value, attrs[i].oa_value_len); + kmem_free(attrs[i].oa_value, attrs[i].oa_value_len); + } + kmem_free(attrs, attrs_size); + kmem_free(privkey, sizeof (crypto_key_t)); +} + +/* + * Frees the space for the entry and the keys and certs + * it carries. + */ +void +kssl_free_entry(kssl_entry_t *kssl_entry) +{ + int i; + Certificate_t *cert; + crypto_key_t *privkey; + + if (kssl_entry->ke_no_freeall) { + kmem_free(kssl_entry, sizeof (kssl_entry_t)); + return; + } + + if ((cert = kssl_entry->ke_server_certificate) != NULL) { + certificate_free(cert); + } + + if ((privkey = kssl_entry->ke_private_key) != NULL) { + privateKey_free(privkey); + }; + + for (i = 0; i < kssl_entry->sid_cache_nentries; i++) + mutex_destroy(&(kssl_entry->sid_cache[i].se_lock)); + + kmem_free(kssl_entry->sid_cache, + kssl_entry->sid_cache_nentries * sizeof (kssl_sid_ent_t)); + + ASSERT(kssl_entry->ke_proxy_head == NULL); + ASSERT(kssl_entry->ke_fallback_head == NULL); + + kmem_free(kssl_entry, sizeof (kssl_entry_t)); +} + +/* + * Returns the index of the entry in kssl_entry_tab[] that matches + * the address and port. Returns -1 if no match is found. + */ +static int +kssl_find_entry(ipaddr_t laddr, in_port_t port, int type, + boolean_t wild_card_match) +{ + int i; + kssl_entry_t *ep; + + ASSERT(MUTEX_HELD(&kssl_tab_mutex)); + + for (i = 0; i < kssl_entry_tab_size; i++) { + ep = kssl_entry_tab[i]; + if (ep == NULL) + continue; + + if (!((type == IS_SSL_PORT && ep->ke_ssl_port == port) || + (type == IS_PROXY_PORT && ep->ke_proxy_port == port))) + continue; + + if ((ep->ke_laddr == laddr) || (wild_card_match && + ((laddr == INADDR_ANY) || (ep->ke_laddr == INADDR_ANY)))) + break; + } + + if (i == kssl_entry_tab_size) + return (-1); + + return (i); +} + +static void +copy_int_to_bytearray(int x, uchar_t *buf) +{ + buf[0] = (x >> 16) & 0xff; + buf[1] = (x >> 8) & 0xff; + buf[2] = (x) & 0xff; +} + +static int +extract_certificate(kssl_params_t *kssl_params, Certificate_t **certpp) +{ + int i, len; + uint64_t in_size; + uchar_t *end_pos; + uint32_t ncert; + uint32_t *cert_sizes; + Certificate_t *cert; + char *begin = (char *)kssl_params; + uchar_t *cert_buf; + int cert_buf_len; + uchar_t *cert_from, *cert_to; + + ASSERT(kssl_params); + + in_size = kssl_params->kssl_params_size; + end_pos = (uchar_t *)kssl_params + in_size; + + /* + * Get the certs array. First the array of sizes, then the actual + * certs. + */ + ncert = kssl_params->kssl_certs.sc_count; + + if (ncert == 0) { + /* no certs in here! why did ya call? */ + return (EINVAL); + } + if (in_size < (sizeof (kssl_params_t) + ncert * sizeof (uint32_t))) { + return (EINVAL); + } + + /* Trusting that the system call preserved the 4-byte aligment */ + cert_sizes = (uint32_t *)(begin + + kssl_params->kssl_certs.sc_sizes_offset); + + /* should this be an ASSERT()? */ + if (!IS_P2ALIGNED(cert_sizes, sizeof (uint32_t))) { + return (EINVAL); + } + + len = 0; + for (i = 0; i < ncert; i++) { + if (cert_sizes[i] < 1) { + return (EINVAL); + } + len += cert_sizes[i] + 3; + } + + len += 3; /* length of certificate message without msg header */ + + cert_buf_len = len + 4 + 4; /* add space for msg headers */ + + cert_buf = kmem_alloc(cert_buf_len, KM_SLEEP); + + cert_buf[0] = (uchar_t)certificate; + copy_int_to_bytearray(len, & cert_buf[1]); + copy_int_to_bytearray(len - 3, & cert_buf[4]); + + cert_from = (uchar_t *)(begin + + kssl_params->kssl_certs.sc_certs_offset); + cert_to = &cert_buf[7]; + + for (i = 0; i < ncert; i++) { + copy_int_to_bytearray(cert_sizes[i], cert_to); + cert_to += 3; + + if (cert_from + cert_sizes[i] > end_pos) { + kmem_free(cert_buf, cert_buf_len); + return (EINVAL); + } + + bcopy(cert_from, cert_to, cert_sizes[i]); + cert_from += cert_sizes[i]; + cert_to += cert_sizes[i]; + } + + len += 4; + cert_buf[len] = (uchar_t)server_hello_done; + copy_int_to_bytearray(0, & cert_buf[len + 1]); + + cert = kmem_alloc(sizeof (Certificate_t), KM_SLEEP); + cert->msg = cert_buf; + cert->len = cert_buf_len; + + *certpp = cert; + + return (0); +} + +static int +extract_private_key(kssl_params_t *kssl_params, crypto_key_t **privkey) +{ + char *begin = (char *)kssl_params; + char *end_pos; + int i, j, rv; + size_t attrs_size; + crypto_object_attribute_t *newattrs = NULL; + char *mp_attrs; + kssl_object_attribute_t att; + char *attval; + uint32_t attlen; + crypto_key_t *kssl_privkey; + + end_pos = (char *)kssl_params + kssl_params->kssl_params_size; + + kssl_privkey = kmem_alloc(sizeof (crypto_key_t), KM_SLEEP); + + kssl_privkey->ck_format = kssl_params->kssl_privkey.ks_format; + kssl_privkey->ck_count = kssl_params->kssl_privkey.ks_count; + + switch (kssl_privkey->ck_format) { + case CRYPTO_KEY_ATTR_LIST: + break; + case CRYPTO_KEY_RAW: + case CRYPTO_KEY_REFERENCE: + default: + rv = EINVAL; + goto err1; + } + + /* allocate the attributes */ + attrs_size = kssl_privkey->ck_count * + sizeof (crypto_object_attribute_t); + + newattrs = kmem_alloc(attrs_size, KM_NOSLEEP); + if (newattrs == NULL) { + rv = ENOMEM; + goto err1; + } + + mp_attrs = begin + kssl_params->kssl_privkey.ks_attrs_offset; + if (mp_attrs + attrs_size > end_pos) { + rv = EINVAL; + goto err1; + } + + /* Now the individual attributes */ + for (i = 0; i < kssl_privkey->ck_count; i++) { + + bcopy(mp_attrs, &att, sizeof (kssl_object_attribute_t)); + + mp_attrs += sizeof (kssl_object_attribute_t); + + attval = begin + att.ka_value_offset; + attlen = att.ka_value_len; + + if (attval + attlen > end_pos) { + rv = EINVAL; + goto err2; + } + + newattrs[i].oa_type = att.ka_type; + newattrs[i].oa_value_len = attlen; + newattrs[i].oa_value = kmem_alloc(attlen, KM_NOSLEEP); + if (newattrs[i].oa_value == NULL) { + rv = ENOMEM; + goto err2; + } + + bcopy(attval, newattrs[i].oa_value, attlen); + } + + kssl_privkey->ck_attrs = newattrs; + + *privkey = kssl_privkey; + + return (0); + +err2: + for (j = 0; j < i; j++) { + kmem_free(newattrs[j].oa_value, newattrs[j].oa_value_len); + } + kmem_free(newattrs, attrs_size); +err1: + kmem_free(kssl_privkey, sizeof (crypto_key_t)); + return (rv); +} + +static kssl_entry_t * +create_kssl_entry(kssl_params_t *kssl_params, Certificate_t *cert, + crypto_key_t *privkey) +{ + int i; + uint16_t s; + kssl_entry_t *kssl_entry; + uint_t cnt, mech_count; + crypto_mech_name_t *mechs; + boolean_t got_rsa, got_md5, got_sha1, got_rc4, got_des, got_3des; + + kssl_entry = kmem_zalloc(sizeof (kssl_entry_t), KM_SLEEP); + + kssl_entry->ke_laddr = kssl_params->kssl_addr.sin_addr.s_addr; + kssl_entry->ke_ssl_port = kssl_params->kssl_addr.sin_port; + kssl_entry->ke_proxy_port = kssl_params->kssl_proxy_port; + if (kssl_params->kssl_session_cache_timeout == 0) + kssl_entry->sid_cache_timeout = DEFAULT_SID_TIMEOUT; + else + kssl_entry->sid_cache_timeout = + kssl_params->kssl_session_cache_timeout; + if (kssl_params->kssl_session_cache_size == 0) + kssl_entry->sid_cache_nentries = DEFAULT_SID_CACHE_NENTRIES; + else + kssl_entry->sid_cache_nentries = + kssl_params->kssl_session_cache_size; + kssl_entry->ke_private_key = privkey; + kssl_entry->ke_server_certificate = cert; + + mechs = crypto_get_mech_list(&mech_count, KM_SLEEP); + if (mechs != NULL) { + got_rsa = got_md5 = got_sha1 = got_rc4 = + got_des = got_3des = B_FALSE; + for (i = 0; i < mech_count; i++) { + if (strncmp(SUN_CKM_RSA_X_509, mechs[i], + CRYPTO_MAX_MECH_NAME) == 0) + got_rsa = B_TRUE; + else if (strncmp(SUN_CKM_MD5_HMAC, mechs[i], + CRYPTO_MAX_MECH_NAME) == 0) + got_md5 = B_TRUE; + else if (strncmp(SUN_CKM_SHA1_HMAC, mechs[i], + CRYPTO_MAX_MECH_NAME) == 0) + got_sha1 = B_TRUE; + else if (strncmp(SUN_CKM_RC4, mechs[i], + CRYPTO_MAX_MECH_NAME) == 0) + got_rc4 = B_TRUE; + else if (strncmp(SUN_CKM_DES_CBC, mechs[i], + CRYPTO_MAX_MECH_NAME) == 0) + got_des = B_TRUE; + else if (strncmp(SUN_CKM_DES3_CBC, mechs[i], + CRYPTO_MAX_MECH_NAME) == 0) + got_3des = B_TRUE; + } + + cnt = 0; + for (i = 0; i < CIPHER_SUITE_COUNT - 1; i++) { + switch (s = kssl_params->kssl_suites[i]) { + case SSL_RSA_WITH_RC4_128_MD5: + if (got_rsa && got_rc4 && got_md5) + kssl_entry->kssl_cipherSuites[cnt++] = s; + break; + case SSL_RSA_WITH_RC4_128_SHA: + if (got_rsa && got_rc4 && got_sha1) + kssl_entry->kssl_cipherSuites[cnt++] = s; + break; + case SSL_RSA_WITH_DES_CBC_SHA: + if (got_rsa && got_des && got_sha1) + kssl_entry->kssl_cipherSuites[cnt++] = s; + break; + case SSL_RSA_WITH_3DES_EDE_CBC_SHA: + if (got_rsa && got_3des && got_sha1) + kssl_entry->kssl_cipherSuites[cnt++] = s; + break; + case CIPHER_NOTSET: + default: + break; + } + } + + crypto_free_mech_list(mechs, mech_count); + } + + /* Add the no encryption suite to the end */ + kssl_entry->kssl_cipherSuites[cnt++] = SSL_RSA_WITH_NULL_SHA; + kssl_entry->kssl_cipherSuites_nentries = cnt; + for (i = 0; i < cnt; i++) + kssl_entry->kssl_saved_Suites[i] = + kssl_entry->kssl_cipherSuites[i]; + + kssl_entry->sid_cache = kmem_alloc( + kssl_entry->sid_cache_nentries * sizeof (kssl_sid_ent_t), KM_SLEEP); + + for (i = 0; i < kssl_entry->sid_cache_nentries; i++) { + mutex_init(&(kssl_entry->sid_cache[i].se_lock), NULL, + MUTEX_DEFAULT, NULL); + kssl_entry->sid_cache[i].se_used = 0; + kssl_entry->sid_cache[i].se_sid.cached = B_FALSE; + } + + KSSL_ENTRY_REFHOLD(kssl_entry); + + return (kssl_entry); +} + +int +kssl_add_entry(kssl_params_t *kssl_params) +{ + int rv, index, i; + Certificate_t *cert; + crypto_key_t *privkey; + kssl_entry_t *kssl_entry; + ipaddr_t laddr; + + if ((rv = extract_certificate(kssl_params, &cert)) != 0) { + return (rv); + } + + if ((rv = extract_private_key(kssl_params, &privkey)) != 0) { + certificate_free(cert); + return (rv); + } + + kssl_entry = create_kssl_entry(kssl_params, cert, privkey); + + /* Revisit here for IPv6 support */ + laddr = kssl_params->kssl_addr.sin_addr.s_addr; + +retry: + mutex_enter(&kssl_tab_mutex); + /* Allocate the array first time here */ + if (kssl_entry_tab == NULL) { + size_t allocsize; + kssl_entry_t **tmp_tab; + int tmp_size; + + tmp_size = KSSL_TAB_INITSIZE; + allocsize = tmp_size * sizeof (kssl_entry_t *); + mutex_exit(&kssl_tab_mutex); + tmp_tab = kmem_zalloc(allocsize, KM_SLEEP); + mutex_enter(&kssl_tab_mutex); + if (kssl_entry_tab != NULL) { + mutex_exit(&kssl_tab_mutex); + kmem_free(tmp_tab, allocsize); + goto retry; + } + kssl_entry_tab_size = tmp_size; + kssl_entry_tab = tmp_tab; + index = 0; + } else { + /* Check if a matching entry exists already */ + index = kssl_find_entry(laddr, + kssl_params->kssl_addr.sin_port, IS_SSL_PORT, B_TRUE); + + if (index == -1) { + /* Check if an entry with the same proxy port exists */ + if (kssl_find_entry(laddr, kssl_params->kssl_proxy_port, + IS_PROXY_PORT, B_TRUE) != -1) { + mutex_exit(&kssl_tab_mutex); + kssl_free_entry(kssl_entry); + return (EADDRINUSE); + } + + /* No matching entry, find an empty spot */ + for (i = 0; i < kssl_entry_tab_size; i++) { + if (kssl_entry_tab[i] == NULL) + break; + } + /* Table full. Gotta grow it */ + if (i == kssl_entry_tab_size) { + kssl_entry_t **new_tab, **old_tab; + size_t allocsize; + size_t oldtabsize = kssl_entry_tab_size * + sizeof (kssl_entry_t *); + int tmp_size, old_size; + + tmp_size = old_size = kssl_entry_tab_size; + tmp_size += KSSL_TAB_INITSIZE; + allocsize = tmp_size * sizeof (kssl_entry_t *); + mutex_exit(&kssl_tab_mutex); + new_tab = kmem_zalloc(allocsize, KM_SLEEP); + mutex_enter(&kssl_tab_mutex); + if (kssl_entry_tab_size > old_size) { + mutex_exit(&kssl_tab_mutex); + kmem_free(new_tab, allocsize); + goto retry; + } + + kssl_entry_tab_size = tmp_size; + bcopy(kssl_entry_tab, new_tab, oldtabsize); + + old_tab = kssl_entry_tab; + kssl_entry_tab = new_tab; + + kmem_free(old_tab, oldtabsize); + } + index = i; + } else { + /* + * We do not want an entry with a specific address and + * an entry with IN_ADDR_ANY to coexist. We could + * replace the existing entry. But, most likely this + * is misconfiguration. Better bail out with an error. + */ + if ((laddr == INADDR_ANY && + (kssl_entry_tab[index]->ke_laddr != INADDR_ANY)) || + (laddr != INADDR_ANY && + (kssl_entry_tab[index]->ke_laddr == INADDR_ANY))) { + mutex_exit(&kssl_tab_mutex); + kssl_free_entry(kssl_entry); + return (EEXIST); + } + + /* Replace the existing entry */ + KSSL_ENTRY_REFRELE(kssl_entry_tab[index]); + kssl_entry_tab[index] = NULL; + kssl_entry_tab_nentries--; + } + } + + kssl_entry_tab[index] = kssl_entry; + kssl_entry_tab_nentries++; + mutex_exit(&kssl_tab_mutex); + + return (0); +} + +int +kssl_delete_entry(struct sockaddr_in *kssl_addr) +{ + ipaddr_t laddr; + int index; + + /* Revisit here for IPv6 support */ + laddr = kssl_addr->sin_addr.s_addr; + + mutex_enter(&kssl_tab_mutex); + index = kssl_find_entry(laddr, kssl_addr->sin_port, + IS_SSL_PORT, B_FALSE); + + if (index == -1) { + mutex_exit(&kssl_tab_mutex); + return (ENOENT); + } + + KSSL_ENTRY_REFRELE(kssl_entry_tab[index]); + kssl_entry_tab[index] = NULL; + kssl_entry_tab_nentries--; + + mutex_exit(&kssl_tab_mutex); + + return (0); +} diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/common/inet/kssl/ksslproto.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/inet/kssl/ksslproto.h Sat Nov 12 18:58:05 2005 -0800 @@ -0,0 +1,355 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _INET_KSSL_KSSLPROTO_H +#define _INET_KSSL_KSSLPROTO_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include +#include /* Cipher suite definitions */ +#include +#include + +#define SSL3_RANDOM_LENGTH 32 +#define SSL3_SESSIONID_BYTES 32 +#define SSL3_HDR_LEN 5 +#define SSL3_MAX_RECORD_LENGTH 16384 +#define SSL3_PRE_MASTER_SECRET_LEN 48 +#define SSL3_MASTER_SECRET_LEN 48 +#define SSL3_MD5_PAD_LEN 48 +#define SSL3_SHA1_PAD_LEN 40 + +#define SSL_MIN_CHALLENGE_BYTES 16 +#define SSL_MAX_CHALLENGE_BYTES 32 + +#define SHA1_HASH_LEN 20 +#define MD5_HASH_LEN 16 +#define MAX_HASH_LEN SHA1_HASH_LEN + +#define KSSL_READ 0 +#define KSSL_WRITE 1 + +#define KSSL_ENCRYPT 0 +#define KSSL_DECRYPT 1 + +#define MSG_INIT 0 +#define MSG_INIT_LEN 1 +#define MSG_BODY 2 + +#define MAX_KEYBLOCK_LENGTH 112 + +#define TLS_MASTER_SECRET_LABEL "master secret" +#define TLS_CLIENT_WRITE_KEY_LABEL "client write key" +#define TLS_SERVER_WRITE_KEY_LABEL "server write key" +#define TLS_CLIENT_FINISHED_LABEL "client finished" +#define TLS_SERVER_FINISHED_LABEL "server finished" +#define TLS_KEY_EXPANSION_LABEL "key expansion" +#define TLS_IV_BLOCK_LABEL "IV block" +#define TLS_MAX_LABEL_SIZE 24 + +#define TLS_FINISHED_SIZE 12 + +/* + * The following constants try to insure an input buffer is optimally aligned + * for MAC hash computation. SHA1/MD5 code prefers 4 byte alignment of each + * 64byte input block to avoid a copy. Our goal is to reach 4 byte alignment + * starting form the 3rd MAC block (input buffer starts in the 3rd block). The + * 3rd block includes the first 53 (MD5 SSL3 MAC) or 57 (SHA1 SSL3 MAC) bytes + * of the input buffer. This means input buffer should start at offset 3 + * within a 4 byte word so that its next block is 4 byte aligned. Since the + * SSL3 record header is 5 bytes long it should start at at offset 2 within a + * 4 byte word. To insure the next record (for buffers that don't fit into 1 + * SSL3 record) also starts at offset 2 within a 4 byte word the previous + * record length should be 3 mod 8 since 5 + 3 mod 8 is 0 i.e. the next record + * starts at the same offset within a 4 byte word as the the previous record. + */ +#define SSL3_MAX_OPTIMAL_RECORD_LENGTH (SSL3_MAX_RECORD_LENGTH - 1) +#define SSL3_OPTIMAL_RECORD_ALIGNMENT 2 + +/* session state */ +typedef struct sslSessionIDStr { + uchar_t session_id[SSL3_SESSIONID_BYTES]; + uchar_t master_secret[SSL3_MASTER_SECRET_LEN]; + clock_t time; + ipaddr_t client_addr; + boolean_t cached; + uint16_t cipher_suite; +} sslSessionID; + +/* An element of the session cache */ +typedef struct kssl_sid_ent { + kmutex_t se_lock; + uint64_t se_used; /* Counter to check hash distribution */ + sslSessionID se_sid; + uchar_t pad[2 * 64 - sizeof (kmutex_t) - sizeof (uint64_t) \ + - sizeof (sslSessionID)]; +} kssl_sid_ent_t; + +typedef struct RC4ContextStr { + uchar_t i; + uchar_t j; + uchar_t S[256]; +} RC4Context; + +typedef enum { + content_change_cipher_spec = 20, + content_alert = 21, + content_handshake = 22, + content_application_data = 23, + content_handshake_v2 = 128 +} SSL3ContentType; + +typedef enum { + hello_request = 0, + client_hello = 1, + server_hello = 2, + certificate = 11, + server_key_exchange = 12, + certificate_request = 13, + server_hello_done = 14, + certificate_verify = 15, + client_key_exchange = 16, + finished = 20 +} SSL3HandshakeType; + +typedef struct SSL3HandshakeMsgStr { + int state; + SSL3HandshakeType type; + int msglen; + int msglen_bytes; + mblk_t *head; + mblk_t *tail; +} SSL3HandshakeMsg; + +typedef struct KSSLJOBStr { + struct ssl_s *ssl; + crypto_req_id_t kjob; + char *buf; + size_t buflen; + int status; +} KSSLJOB; + +typedef struct KSSLMACJOBStr { + struct ssl_s *ssl; + buf_t *in; + buf_t *out; + uchar_t *rstart; + int rlen; + uint64_t seq; + SSL3ContentType ct; + uchar_t *digest; + int dir; +} KSSLMACJOB; + + +typedef struct { + uchar_t md5[MD5_HASH_LEN]; + uchar_t sha1[SHA1_HASH_LEN]; + uchar_t tlshash[TLS_FINISHED_SIZE]; +} SSL3Hashes; + +typedef enum { + close_notify = 0, + unexpected_message = 10, + bad_record_mac = 20, + decompression_failure = 30, + handshake_failure = 40, + no_certificate = 41, + bad_certificate = 42, + unsupported_certificate = 43, + certificate_revoked = 44, + certificate_expired = 45, + certificate_unknown = 46, + illegal_parameter = 47, + unknown_ca = 48, + access_denied = 49, + decode_error = 50, + decrypt_error = 51, + export_restriction = 60, + protocol_version = 70, + insufficient_security = 71, + internal_error = 80, + user_canceled = 90, + no_renegotiation = 100 +} SSL3AlertDescription; + +typedef enum { + alert_warning = 1, + alert_fatal = 2 +} SSL3AlertLevel; + +typedef enum { + wait_client_hello = 0, + wait_client_key = 1, + wait_client_key_done = 2, + wait_change_cipher = 3, + wait_finished = 4, + idle_handshake = 5 +} SSL3WaitState; + +typedef enum { + sender_client = 0x434c4e54, + sender_server = 0x53525652 +} SSL3Sender; + +typedef enum { + mac_md5 = 0, + mac_sha = 1 +} SSL3MACAlgorithm; + +/* The SSL bulk cipher definition */ +typedef enum { + cipher_null = 0, + cipher_rc4 = 1, + cipher_des = 2, + cipher_3des = 3 +} SSL3BulkCipher; + +typedef enum { type_stream = 0, type_block = 1 } CipherType; + +typedef struct ssl3CipherSuiteDefStr { + uint16_t suite; + SSL3BulkCipher calg; + SSL3MACAlgorithm malg; + int keyblksz; +} ssl3CipherSuiteDef; + +typedef void (*hashinit_func_t)(void *); +typedef void (*hashupdate_func_t)(void *, uchar_t *, uint32_t); +typedef void (*hashfinal_func_t)(uchar_t *, void *); + +typedef struct KSSLMACDefStr { + int hashsz; + int padsz; + hashinit_func_t HashInit; + hashupdate_func_t HashUpdate; + hashfinal_func_t HashFinal; +} KSSLMACDef; + +typedef struct KSSLCipherDefStr { + CipherType type; + int bsize; + int keysz; + crypto_mech_type_t mech_type; +} KSSLCipherDef; + +typedef union KSSL_HASHCTXUnion { + SHA1_CTX sha; + MD5_CTX md5; +} KSSL_HASHCTX; + +typedef struct KSSLCipherSpecStr { + int mac_hashsz; + int mac_padsz; + void (*MAC_HashInit)(void *); + void (*MAC_HashUpdate)(void *, uchar_t *, uint32_t); + void (*MAC_HashFinal)(uchar_t *, void *); + + CipherType cipher_type; + int cipher_bsize; + int cipher_keysz; + + crypto_mechanism_t cipher_mech; + crypto_mechanism_t hmac_mech; /* for TLS */ + crypto_key_t cipher_key; + crypto_key_t hmac_key; /* for TLS */ + + crypto_context_t cipher_ctx; + crypto_data_t cipher_data; + +} KSSLCipherSpec; + +/* + * SSL connection state. This one hangs off of a tcp_t structure. + */ +typedef struct ssl_s { + kmutex_t kssl_lock; + struct kssl_entry_s *kssl_entry; + mblk_t *rec_ass_head; + mblk_t *rec_ass_tail; + uint_t kssl_refcnt; + ipaddr_t faddr; + uint32_t tcp_mss; + SSL3WaitState hs_waitstate; + boolean_t resumed; + boolean_t close_notify; + boolean_t fatal_alert; + boolean_t fatal_error; + boolean_t alert_sent; + boolean_t appdata_sent; + boolean_t activeinput; + SSL3AlertLevel sendalert_level; + SSL3AlertDescription sendalert_desc; + mblk_t *handshake_sendbuf; + mblk_t *alert_sendbuf; + kssl_callback_t cke_callback_func; + void *cke_callback_arg; + uint32_t macjobs_todo; + uint32_t macjobs_done; + uint16_t pending_cipher_suite; + SSL3MACAlgorithm pending_malg; + SSL3BulkCipher pending_calg; + int pending_keyblksz; + uint64_t seq_num[2]; + SSL3HandshakeMsg msg; + KSSLJOB job; + KSSLCipherSpec spec[2]; + uchar_t pending_keyblock[MAX_KEYBLOCK_LENGTH]; + uchar_t mac_secret[2][MAX_HASH_LEN]; + KSSL_HASHCTX mac_ctx[2][2]; /* inner 'n outer per dir */ + sslSessionID sid; + SHA1_CTX hs_sha1; + MD5_CTX hs_md5; + SSL3Hashes hs_hashes; + uchar_t client_random[SSL3_RANDOM_LENGTH]; + uchar_t server_random[SSL3_RANDOM_LENGTH]; + int sslcnt; + uchar_t major_version; + uchar_t minor_version; +} ssl_t; + +#define IS_TLS(s) (s->major_version == 3 && s->minor_version == 1) + +#define SSL3_REC_SIZE(mp) (uint8_t *)(mp)->b_rptr + 3 + +extern int kssl_spec_init(ssl_t *, int); +extern void kssl_send_alert(ssl_t *, SSL3AlertLevel, SSL3AlertDescription); + +#ifdef __cplusplus +} +#endif + +#endif /* _INET_KSSL_KSSLPROTO_H */ diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/common/inet/kssl/ksslrec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/inet/kssl/ksslrec.c Sat Nov 12 18:58:05 2005 -0800 @@ -0,0 +1,2077 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#define _SUN_TPI_VERSION 2 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "ksslimpl.h" +#include "ksslapi.h" +#include "ksslproto.h" +#include "kssldebug.h" + +static ssl3CipherSuiteDef cipher_suite_defs[] = { + {SSL_RSA_WITH_RC4_128_SHA, cipher_rc4, mac_sha, 72}, + {SSL_RSA_WITH_RC4_128_MD5, cipher_rc4, mac_md5, 64}, + {SSL_RSA_WITH_DES_CBC_SHA, cipher_des, mac_sha, 72}, + {SSL_RSA_WITH_3DES_EDE_CBC_SHA, cipher_3des, mac_sha, 104}, + {SSL_RSA_WITH_NULL_SHA, cipher_null, mac_sha, 40} +}; + +static int cipher_suite_defs_nentries = + sizeof (cipher_suite_defs) / sizeof (cipher_suite_defs[0]); + +static KSSLMACDef mac_defs[] = { /* indexed by SSL3MACAlgorithm */ + /* macsz padsz HashInit HashUpdate HashFinal */ + + {MD5_HASH_LEN, SSL3_MD5_PAD_LEN, + (hashinit_func_t)MD5Init, (hashupdate_func_t)MD5Update, + (hashfinal_func_t)MD5Final}, + + {SHA1_HASH_LEN, SSL3_SHA1_PAD_LEN, + (hashinit_func_t)SHA1Init, (hashupdate_func_t)SHA1Update, + (hashfinal_func_t)SHA1Final}, +}; + +static uchar_t kssl_pad_1[60] = { + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36 +}; +static uchar_t kssl_pad_2[60] = { + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c +}; + +int kssl_debug; +int kssl_cache_count; +static boolean_t kssl_synchronous = B_FALSE; + +static void kssl_update_handshake_hashes(ssl_t *, uchar_t *, uint_t); +static int kssl_compute_handshake_hashes(ssl_t *, SSL3Hashes *, uint32_t); +static int kssl_handle_client_hello(ssl_t *, mblk_t *, int); +static int kssl_handle_client_key_exchange(ssl_t *, mblk_t *, int, + kssl_callback_t, void *); +static int kssl_send_server_hello(ssl_t *); +static int kssl_send_certificate_and_server_hello_done(ssl_t *); +static int kssl_send_change_cipher_specs(ssl_t *); +static int kssl_send_finished(ssl_t *, int); +static int kssl_handle_finished(ssl_t *, mblk_t *, int); +static void kssl_get_hello_random(uchar_t *); +static uchar_t *kssl_rsa_unwrap(uchar_t *, size_t *); +static void kssl_cache_sid(sslSessionID *, kssl_entry_t *); +static void kssl_lookup_sid(sslSessionID *, uchar_t *, ipaddr_t, + kssl_entry_t *); +static int kssl_generate_tls_ms(ssl_t *, uchar_t *, size_t); +static void kssl_generate_ssl_ms(ssl_t *, uchar_t *, size_t); +static int kssl_generate_tls_keyblock(ssl_t *); +static void kssl_generate_keyblock(ssl_t *); +static void kssl_ssl3_key_material_derive_step(ssl_t *, uchar_t *, size_t, + int, uchar_t *, int); +static int kssl_tls_PRF(ssl_t *, uchar_t *, size_t, + uchar_t *, size_t, uchar_t *, size_t, uchar_t *, size_t); +static int kssl_tls_P_hash(crypto_mechanism_t *, crypto_key_t *, + size_t, uchar_t *, size_t, uchar_t *, size_t, uchar_t *, size_t); +static void kssl_cke_done(void *, int); + +#define MAX_TLS_KEYBLOCK_SIZE 160 /* more than enough for largest TLS key */ + +#define HMAC_INIT(m, k, c) \ + rv = crypto_mac_init(m, k, NULL, c, NULL); if (CRYPTO_ERR(rv)) goto end; + +#define HMAC_UPDATE(c, d, l) \ + dd.cd_raw.iov_base = (char *)d; \ + dd.cd_length = dd.cd_raw.iov_len = l; \ + rv = crypto_mac_update(c, &dd, NULL); if (CRYPTO_ERR(rv)) goto end; + +#define HMAC_FINAL(c, d, l) \ + mac.cd_raw.iov_base = (char *)d; \ + mac.cd_length = mac.cd_raw.iov_len = l; \ + rv = crypto_mac_final(c, &mac, NULL); if (CRYPTO_ERR(rv)) goto end; + +int +kssl_compute_record_mac( + ssl_t *ssl, + int direction, + uint64_t seq_num, + SSL3ContentType ct, + uchar_t *versionp, + uchar_t *buf, + int len, + uchar_t *digest) +{ + KSSL_HASHCTX mac_ctx; + KSSL_HASHCTX *ctx = &mac_ctx; + uchar_t temp[16], *p; + KSSLCipherSpec *spec; + int rv = 0; + + spec = &ssl->spec[direction]; + + if (spec->mac_hashsz == 0) { + return (1); + } + + /* mac_secret = ssl->mac_secret[direction]; */ + + p = temp; + + *p++ = (seq_num >> 56) & 0xff; + *p++ = (seq_num >> 48) & 0xff; + *p++ = (seq_num >> 40) & 0xff; + *p++ = (seq_num >> 32) & 0xff; + *p++ = (seq_num >> 24) & 0xff; + *p++ = (seq_num >> 16) & 0xff; + *p++ = (seq_num >> 8) & 0xff; + *p++ = (seq_num) & 0xff; + *p++ = (uchar_t)ct; + if (IS_TLS(ssl)) { + *p++ = versionp[0]; + *p++ = versionp[1]; + } + *p++ = (len >> 8) & 0xff; + *p++ = (len) & 0xff; + + if (IS_TLS(ssl)) { + crypto_data_t dd, mac; + crypto_context_t ctx; + + dd.cd_format = CRYPTO_DATA_RAW; + dd.cd_offset = 0; + mac.cd_format = CRYPTO_DATA_RAW; + mac.cd_offset = 0; + + HMAC_INIT(&spec->hmac_mech, &spec->hmac_key, &ctx); + HMAC_UPDATE(ctx, temp, p - temp); + HMAC_UPDATE(ctx, buf, len); + HMAC_FINAL(ctx, digest, spec->mac_hashsz); +end: + if (CRYPTO_ERR(rv)) { +#ifdef DEBUG + cmn_err(CE_WARN, + "kssl_compute_record_mac - crypto_mac error " + "0x%0x", rv); +#endif /* DEBUG */ + KSSL_COUNTER(compute_mac_failure, 1); + } + } else { + bcopy(&(ssl->mac_ctx[direction][0]), ctx, + sizeof (KSSL_HASHCTX)); + spec->MAC_HashUpdate((void *)ctx, temp, p - temp); + spec->MAC_HashUpdate((void *)ctx, buf, len); + spec->MAC_HashFinal(digest, (void *)ctx); + + bcopy(&(ssl->mac_ctx[direction][1]), ctx, + sizeof (KSSL_HASHCTX)); + spec->MAC_HashUpdate((void *)ctx, digest, spec->mac_hashsz); + spec->MAC_HashFinal(digest, (void *)ctx); + } + + return (rv); +} + +/* + * Handles handshake messages. + * Messages to be replied are returned in handshake_sendbuf. + */ +int +kssl_handle_handshake_message(ssl_t *ssl, mblk_t *mp, int *err, + kssl_callback_t cbfn, void *arg) +{ + uint32_t msglen; + uchar_t msghdr[4]; + + ASSERT(ssl->msg.state == MSG_BODY); + ASSERT(ssl->msg.msglen_bytes == 3); + ASSERT(mp->b_wptr >= mp->b_rptr + ssl->msg.msglen); + + ssl->sslcnt++; + msglen = ssl->msg.msglen; + + if (ssl->msg.type == client_hello) { + MD5Init(&ssl->hs_md5); + SHA1Init(&ssl->hs_sha1); + } + + if (ssl->msg.type == finished && ssl->resumed == B_FALSE) { + if (kssl_compute_handshake_hashes(ssl, &ssl->hs_hashes, + sender_client) != 0) { + *err = SSL_MISS; + return (0); + } + } + + if (ssl->msg.type != finished || ssl->resumed == B_FALSE) { + msghdr[0] = (uchar_t)ssl->msg.type; + + msghdr[1] = (uchar_t)(msglen >> 16); + msghdr[2] = (uchar_t)(msglen >> 8); + msghdr[3] = (uchar_t)(msglen); + kssl_update_handshake_hashes(ssl, msghdr, 4); + kssl_update_handshake_hashes(ssl, mp->b_rptr, msglen); + } + + ssl->msg.state = MSG_INIT; + ssl->msg.msglen = 0; + ssl->msg.msglen_bytes = 0; + + switch (ssl->msg.type) { + case client_hello: + if (ssl->hs_waitstate != wait_client_hello) { + kssl_send_alert(ssl, alert_fatal, + unexpected_message); + *err = EBADMSG; + ssl->activeinput = B_FALSE; + return (1); + } + *err = kssl_handle_client_hello(ssl, mp, msglen); + if (*err == SSL_MISS) { + ssl->activeinput = B_FALSE; + return (0); + } + return (1); + case client_key_exchange: + if (ssl->hs_waitstate != wait_client_key) { + kssl_send_alert(ssl, alert_fatal, + unexpected_message); + *err = EBADMSG; + ssl->activeinput = B_FALSE; + return (1); + } + *err = kssl_handle_client_key_exchange(ssl, mp, + msglen, cbfn, arg); + return (1); + case finished: + if (ssl->hs_waitstate != wait_finished) { + kssl_send_alert(ssl, alert_fatal, + unexpected_message); + *err = EBADMSG; + ssl->activeinput = B_FALSE; + return (1); + } + *err = kssl_handle_finished(ssl, mp, msglen); + return (1); + default: + kssl_send_alert(ssl, alert_fatal, unexpected_message); + ssl->activeinput = B_FALSE; + *err = EBADMSG; + return (1); + } +} + +static void +kssl_update_handshake_hashes(ssl_t *ssl, uchar_t *buf, uint_t len) +{ + MD5Update(&ssl->hs_md5, buf, len); + SHA1Update(&ssl->hs_sha1, buf, len); +} + +static int +kssl_compute_handshake_hashes( + ssl_t *ssl, + SSL3Hashes *hashes, + uint32_t sender) +{ + MD5_CTX md5 = ssl->hs_md5; /* clone md5 context */ + SHA1_CTX sha1 = ssl->hs_sha1; /* clone sha1 context */ + MD5_CTX *md5ctx = &md5; + SHA1_CTX *sha1ctx = &sha1; + + if (IS_TLS(ssl)) { + uchar_t seed[MD5_HASH_LEN + SHA1_HASH_LEN]; + char *label; + + /* + * Do not take another hash step here. + * Just complete the operation. + */ + MD5Final(hashes->md5, md5ctx); + SHA1Final(hashes->sha1, sha1ctx); + + bcopy(hashes->md5, seed, MD5_HASH_LEN); + bcopy(hashes->sha1, seed + MD5_HASH_LEN, SHA1_HASH_LEN); + + if (sender == sender_client) + label = TLS_CLIENT_FINISHED_LABEL; + else + label = TLS_SERVER_FINISHED_LABEL; + + return (kssl_tls_PRF(ssl, + ssl->sid.master_secret, (size_t)SSL3_MASTER_SECRET_LEN, + (uchar_t *)label, strlen(label), + seed, (size_t)(MD5_HASH_LEN + SHA1_HASH_LEN), + hashes->tlshash, (size_t)TLS_FINISHED_SIZE)); + } else { + uchar_t s[4]; + s[0] = (sender >> 24) & 0xff; + s[1] = (sender >> 16) & 0xff; + s[2] = (sender >> 8) & 0xff; + s[3] = (sender) & 0xff; + + MD5Update(md5ctx, s, 4); + MD5Update(md5ctx, ssl->sid.master_secret, + SSL3_MASTER_SECRET_LEN); + MD5Update(md5ctx, kssl_pad_1, SSL3_MD5_PAD_LEN); + MD5Final(hashes->md5, md5ctx); + + MD5Init(md5ctx); + MD5Update(md5ctx, ssl->sid.master_secret, + SSL3_MASTER_SECRET_LEN); + MD5Update(md5ctx, kssl_pad_2, SSL3_MD5_PAD_LEN); + MD5Update(md5ctx, hashes->md5, MD5_HASH_LEN); + MD5Final(hashes->md5, md5ctx); + + SHA1Update(sha1ctx, s, 4); + SHA1Update(sha1ctx, ssl->sid.master_secret, + SSL3_MASTER_SECRET_LEN); + SHA1Update(sha1ctx, kssl_pad_1, SSL3_SHA1_PAD_LEN); + SHA1Final(hashes->sha1, sha1ctx); + + SHA1Init(sha1ctx); + SHA1Update(sha1ctx, ssl->sid.master_secret, + SSL3_MASTER_SECRET_LEN); + SHA1Update(sha1ctx, kssl_pad_2, SSL3_SHA1_PAD_LEN); + SHA1Update(sha1ctx, hashes->sha1, SHA1_HASH_LEN); + SHA1Final(hashes->sha1, sha1ctx); + return (0); + } +} + + +#define KSSL_SSL3_CH_MIN_MSGLEN (39) + +static int +kssl_handle_client_hello(ssl_t *ssl, mblk_t *mp, int msglen) +{ + uchar_t *msgend; + int err; + SSL3AlertDescription desc = illegal_parameter; + uint_t sidlen; + uint_t nsuites; + uchar_t *suitesp; + uint_t i, j; + uint16_t suite; + int ch_msglen = KSSL_SSL3_CH_MIN_MSGLEN; + + ASSERT(mp->b_wptr >= mp->b_rptr + msglen); + ASSERT(ssl->msg.type == client_hello); + ASSERT(ssl->hs_waitstate == wait_client_hello); + ASSERT(ssl->resumed == B_FALSE); + + if (msglen < ch_msglen) { + goto falert; + } + + msgend = mp->b_rptr + msglen; + + /* Support SSLv3 (version == 3.0) or TLS (version == 3.1) */ + if (ssl->major_version != 3 || (ssl->major_version == 3 && + ssl->minor_version != 0 && ssl->minor_version != 1)) { + KSSL_DEBUG3_IF(kssl_debug, + "HandleClientHello: handshake failure - " + "SSL version not supported (%d %d)", + ssl->major_version, ssl->minor_version); + desc = handshake_failure; + goto falert; + } + mp->b_rptr += 2; /* skip the version bytes */ + + bcopy(mp->b_rptr, ssl->client_random, SSL3_RANDOM_LENGTH); + mp->b_rptr += SSL3_RANDOM_LENGTH; + + ASSERT(ssl->sid.cached == B_FALSE); + sidlen = *mp->b_rptr++; + ch_msglen += sidlen; + if (msglen < ch_msglen) { + goto falert; + } + if (sidlen != SSL3_SESSIONID_BYTES) { + mp->b_rptr += sidlen; + } else { + kssl_lookup_sid(&ssl->sid, mp->b_rptr, ssl->faddr, + ssl->kssl_entry); + mp->b_rptr += SSL3_SESSIONID_BYTES; + } + + nsuites = ((uint_t)mp->b_rptr[0] << 8) + (uint_t)mp->b_rptr[1]; + mp->b_rptr += 2; + ch_msglen += nsuites; + if (msglen != ch_msglen) { + goto falert; + } + if (nsuites & 0x1) { + goto falert; + } + suitesp = mp->b_rptr; + if (ssl->sid.cached == B_TRUE) { + suite = ssl->sid.cipher_suite; + for (j = 0; j < nsuites; j += 2) { + if (suitesp[j] == ((suite >> 8) & 0xff) && + suitesp[j + 1] == (suite & 0xff)) { + break; + } + } + if (j < nsuites) { + goto suite_found; + } + kssl_uncache_sid(&ssl->sid, ssl->kssl_entry); + ssl->sid.cached = B_FALSE; + } + + /* Check if this server is capable of the cipher suite */ + for (i = 0; i < ssl->kssl_entry->kssl_cipherSuites_nentries; i++) { + suite = ssl->kssl_entry->kssl_cipherSuites[i]; + for (j = 0; j < nsuites; j += 2) { + if (suitesp[j] == ((suite >> 8) & 0xff) && + suitesp[j + 1] == (suite & 0xff)) { + break; + } + } + if (j < nsuites) { + break; + } + } + if (i == ssl->kssl_entry->kssl_cipherSuites_nentries) { + if (ssl->sslcnt == 1) { + KSSL_COUNTER(no_suite_found, 1); + return (SSL_MISS); + } + desc = handshake_failure; + KSSL_DEBUG1_IF(kssl_debug, + "kssl_handle_client_hello: no cipher suites found"); + goto falert; + } + +suite_found: + + mp->b_rptr += nsuites; + if (*mp->b_rptr++ != 1 || *mp->b_rptr++ != 0) { + desc = handshake_failure; + KSSL_DEBUG1_IF(kssl_debug, + "kssl_handle_client_hello: handshake failure"); + goto falert; + } + + mp->b_rptr = msgend; + + for (i = 0; i < cipher_suite_defs_nentries; i++) { + if (suite == cipher_suite_defs[i].suite) { + break; + } + } + + ASSERT(i < cipher_suite_defs_nentries); + + ssl->pending_cipher_suite = suite; + ssl->pending_malg = cipher_suite_defs[i].malg; + ssl->pending_calg = cipher_suite_defs[i].calg; + ssl->pending_keyblksz = cipher_suite_defs[i].keyblksz; + + if (ssl->sid.cached == B_TRUE) { + err = kssl_send_server_hello(ssl); + if (err != 0) { + return (err); + } + if (IS_TLS(ssl)) + err = kssl_generate_tls_keyblock(ssl); + else + kssl_generate_keyblock(ssl); + + err = kssl_send_change_cipher_specs(ssl); + if (err != 0) { + return (err); + } + + err = kssl_send_finished(ssl, 1); + if (err != 0) + return (err); + + err = kssl_compute_handshake_hashes(ssl, &ssl->hs_hashes, + sender_client); + if (err != 0) + return (err); + + ssl->hs_waitstate = wait_change_cipher; + ssl->resumed = B_TRUE; + ssl->activeinput = B_FALSE; + KSSL_COUNTER(resumed_sessions, 1); + return (0); + } + + (void) random_get_pseudo_bytes(ssl->sid.session_id, + SSL3_SESSIONID_BYTES); + ssl->sid.client_addr = ssl->faddr; + ssl->sid.cipher_suite = suite; + + err = kssl_send_server_hello(ssl); + if (err != 0) { + return (err); + } + err = kssl_send_certificate_and_server_hello_done(ssl); + if (err != 0) { + return (err); + } + KSSL_COUNTER(full_handshakes, 1); + ssl->hs_waitstate = wait_client_key; + ssl->activeinput = B_FALSE; + return (0); + +falert: + kssl_send_alert(ssl, alert_fatal, desc); + return (EBADMSG); +} + +static void +kssl_cache_sid(sslSessionID *sid, kssl_entry_t *kssl_entry) +{ + uint_t index; + uchar_t *s = sid->session_id; + int l = SSL3_SESSIONID_BYTES - 1; + kmutex_t *lock; + + ASSERT(sid->cached == B_TRUE); + + index = (int)sid->client_addr ^ (((int)s[0] << 24) | ((int)s[1] << 16) | + ((int)s[2] << 8) | (int)s[l]); + + index %= kssl_entry->sid_cache_nentries; + + sid->time = lbolt; + + lock = &(kssl_entry->sid_cache[index].se_lock); + mutex_enter(lock); + kssl_entry->sid_cache[index].se_used++; + bcopy(sid, &(kssl_entry->sid_cache[index].se_sid), sizeof (*sid)); + mutex_exit(lock); +} + +static void +kssl_lookup_sid(sslSessionID *sid, uchar_t *s, ipaddr_t faddr, + kssl_entry_t *kssl_entry) +{ + uint_t index; + int l = SSL3_SESSIONID_BYTES - 1; + kmutex_t *lock; + sslSessionID *csid; + + ASSERT(sid->cached == B_FALSE); + + KSSL_COUNTER(sid_cache_lookups, 1); + + index = (int)faddr ^ (((int)s[0] << 24) | ((int)s[1] << 16) | + ((int)s[2] << 8) | (int)s[l]); + + index %= kssl_entry->sid_cache_nentries; + + lock = &(kssl_entry->sid_cache[index].se_lock); + mutex_enter(lock); + csid = &(kssl_entry->sid_cache[index].se_sid); + if (csid->cached == B_FALSE || csid->client_addr != faddr || + bcmp(csid->session_id, s, SSL3_SESSIONID_BYTES)) { + mutex_exit(lock); + return; + } + + if (TICK_TO_SEC(lbolt - csid->time) > kssl_entry->sid_cache_timeout) { + csid->cached = B_FALSE; + mutex_exit(lock); + return; + } + + bcopy(csid, sid, sizeof (*sid)); + mutex_exit(lock); + ASSERT(sid->cached == B_TRUE); + + KSSL_COUNTER(sid_cache_hits, 1); +} + +static uchar_t * +kssl_rsa_unwrap(uchar_t *buf, size_t *lenp) +{ + size_t len = *lenp; + int i = 2; + + if (buf[0] != 0 || buf[1] != 2) { + return (NULL); + } + + while (i < len) { + if (buf[i++] == 0) { + *lenp = len - i; + break; + } + } + + if (i == len) { + return (NULL); + } + + return (buf + i); +} + +void +kssl_uncache_sid(sslSessionID *sid, kssl_entry_t *kssl_entry) +{ + uint_t index; + uchar_t *s = sid->session_id; + int l = SSL3_SESSIONID_BYTES - 1; + sslSessionID *csid; + kmutex_t *lock; + + ASSERT(sid->cached == B_TRUE); + + KSSL_COUNTER(sid_uncached, 1); + + index = (int)sid->client_addr ^ (((int)s[0] << 24) | ((int)s[1] << 16) | + ((int)s[2] << 8) | (int)s[l]); + + index %= kssl_entry->sid_cache_nentries; + + lock = &(kssl_entry->sid_cache[index].se_lock); + mutex_enter(lock); + csid = &(kssl_entry->sid_cache[index].se_sid); + if (csid->client_addr != sid->client_addr || + bcmp(csid->session_id, s, SSL3_SESSIONID_BYTES)) { + mutex_exit(lock); + return; + } + csid->cached = B_FALSE; + mutex_exit(lock); +} + + +#define KSSL_SSL3_SH_RECLEN (74) +#define KSSL_SSL3_FIN_MSGLEN (36) + +#define KSSL_SSL3_MAX_CCP_FIN_MSGLEN (128) /* comfortable upper bound */ + +static int +kssl_send_server_hello(ssl_t *ssl) +{ + mblk_t *mp; + uchar_t *buf; + uchar_t *msgstart; + + mp = allocb(ssl->tcp_mss, BPRI_HI); + if (mp == NULL) { + KSSL_COUNTER(alloc_fails, 1); + return (ENOMEM); + } + ssl->handshake_sendbuf = mp; + buf = mp->b_wptr; + + /* 5 byte record header */ + buf[0] = content_handshake; + buf[1] = ssl->major_version; + buf[2] = ssl->minor_version; + buf[3] = KSSL_SSL3_SH_RECLEN >> 8; + buf[4] = KSSL_SSL3_SH_RECLEN & 0xff; + buf += SSL3_HDR_LEN; + + msgstart = buf; + + /* 6 byte message header */ + buf[0] = (uchar_t)server_hello; /* message type */ + buf[1] = 0; /* message len byte 0 */ + buf[2] = ((KSSL_SSL3_SH_RECLEN - 4) >> 8) & + 0xff; /* message len byte 1 */ + buf[3] = (KSSL_SSL3_SH_RECLEN - 4) & 0xff; /* message len byte 2 */ + + buf[4] = ssl->major_version; /* version byte 0 */ + buf[5] = ssl->minor_version; /* version byte 1 */ + + buf += 6; + + kssl_get_hello_random(ssl->server_random); + bcopy(ssl->server_random, buf, SSL3_RANDOM_LENGTH); + buf += SSL3_RANDOM_LENGTH; + + buf[0] = SSL3_SESSIONID_BYTES; + bcopy(ssl->sid.session_id, buf + 1, SSL3_SESSIONID_BYTES); + buf += SSL3_SESSIONID_BYTES + 1; + + buf[0] = (ssl->pending_cipher_suite >> 8) & 0xff; + buf[1] = ssl->pending_cipher_suite & 0xff; + + buf[2] = 0; /* No compression */ + + mp->b_wptr = buf + 3; + ASSERT(mp->b_wptr < mp->b_datap->db_lim); + + kssl_update_handshake_hashes(ssl, msgstart, KSSL_SSL3_SH_RECLEN); + return (0); +} + +static void +kssl_get_hello_random(uchar_t *buf) +{ + timestruc_t ts; + time_t sec; + + gethrestime(&ts); + sec = ts.tv_sec; + + buf[0] = (sec >> 24) & 0xff; + buf[1] = (sec >> 16) & 0xff; + buf[2] = (sec >> 8) & 0xff; + buf[3] = (sec) & 0xff; + + (void) random_get_pseudo_bytes(&buf[4], SSL3_RANDOM_LENGTH - 4); + + /* Should this be caching? */ +} + +static int +kssl_tls_P_hash(crypto_mechanism_t *mech, crypto_key_t *key, + size_t hashlen, + uchar_t *label, size_t label_len, + uchar_t *seed, size_t seedlen, + uchar_t *data, size_t datalen) +{ + int rv = 0; + uchar_t A1[MAX_HASH_LEN], result[MAX_HASH_LEN]; + int bytes_left = datalen; + crypto_data_t dd, mac; + crypto_context_t ctx; + + dd.cd_format = CRYPTO_DATA_RAW; + dd.cd_offset = 0; + mac.cd_format = CRYPTO_DATA_RAW; + mac.cd_offset = 0; + + /* + * A(i) = HMAC_hash(secred, seed + A(i-1)); + * A(0) = seed; + * + * Compute A(1): + * A(1) = HMAC_hash(secret, label + seed) + * + */ + HMAC_INIT(mech, key, &ctx); + HMAC_UPDATE(ctx, label, label_len); + HMAC_UPDATE(ctx, seed, seedlen); + HMAC_FINAL(ctx, A1, hashlen); + + /* Compute A(2) ... A(n) */ + while (bytes_left > 0) { + HMAC_INIT(mech, key, &ctx); + HMAC_UPDATE(ctx, A1, hashlen); + HMAC_UPDATE(ctx, label, label_len); + HMAC_UPDATE(ctx, seed, seedlen); + HMAC_FINAL(ctx, result, hashlen); + + /* + * The A(i) value is stored in "result". + * Save the results of the MAC so it can be input to next + * iteration. + */ + if (bytes_left > hashlen) { + /* Store the chunk result */ + bcopy(result, data, hashlen); + data += hashlen; + + bytes_left -= hashlen; + + /* Update A1 for next iteration */ + HMAC_INIT(mech, key, &ctx); + HMAC_UPDATE(ctx, A1, hashlen); + HMAC_FINAL(ctx, A1, hashlen); + + } else { + bcopy(result, data, bytes_left); + data += bytes_left; + bytes_left = 0; + } + } +end: + if (CRYPTO_ERR(rv)) { +#ifdef DEBUG + cmn_err(CE_WARN, "kssl_P_hash: crypto_mac error 0x%02X", rv); +#endif /* DEBUG */ + KSSL_COUNTER(compute_mac_failure, 1); + } + return (rv); +} + +/* ARGSUSED */ +static int +kssl_tls_PRF(ssl_t *ssl, + uchar_t *secret, size_t secret_len, + uchar_t *label, size_t label_len, + uchar_t *seed, size_t seed_len, + uchar_t *prfresult, size_t prfresult_len) +{ + /* + * RFC 2246: + * PRF(secret, label, seed) = P_MD5(S1, label + seed) XOR + * P_SHA1(S2, label + seed); + * S1 = 1st half of secret. + * S1 = 2nd half of secret. + * + */ + + int rv = 0, i; + uchar_t psha1[MAX_TLS_KEYBLOCK_SIZE]; + crypto_key_t S1, S2; + + /* length of secret keys is ceil(length/2) */ + size_t slen = roundup(secret_len, 2) / 2; + + if (prfresult_len > MAX_TLS_KEYBLOCK_SIZE) { + KSSL_DEBUG2_IF(kssl_debug, "kssl_tls_PRF: unexpected keyblock " + "size (%lu)", prfresult_len); + return (CRYPTO_ARGUMENTS_BAD); + } + + ASSERT(prfresult != NULL); + ASSERT(label != NULL); + ASSERT(seed != NULL); + + S1.ck_data = secret; + S1.ck_length = slen * 8; /* bits */ + S1.ck_format = CRYPTO_KEY_RAW; + + S2.ck_data = secret + slen; + S2.ck_length = slen * 8; /* bits */ + S2.ck_format = CRYPTO_KEY_RAW; + + rv = kssl_tls_P_hash(&hmac_md5_mech, &S1, MD5_HASH_LEN, + label, label_len, + seed, seed_len, + prfresult, prfresult_len); + if (CRYPTO_ERR(rv)) + goto end; + + rv = kssl_tls_P_hash(&hmac_sha1_mech, &S2, SHA1_HASH_LEN, + label, label_len, + seed, seed_len, + psha1, prfresult_len); + if (CRYPTO_ERR(rv)) + goto end; + + for (i = 0; i < prfresult_len; i++) + prfresult[i] ^= psha1[i]; + +end: + if (CRYPTO_ERR(rv)) + bzero(prfresult, prfresult_len); + + return (rv); +} + +static int +kssl_generate_tls_ms(ssl_t *ssl, uchar_t *pms, size_t pmslen) +{ + uchar_t seed[SSL3_RANDOM_LENGTH * 2]; + + /* + * Computing the master secret: + * ---------------------------- + * master_secret = PRF (pms, "master secret", + * ClientHello.random + ServerHello.random); + */ + bcopy(ssl->client_random, seed, SSL3_RANDOM_LENGTH); + bcopy(ssl->server_random, seed + SSL3_RANDOM_LENGTH, + SSL3_RANDOM_LENGTH); + + return (kssl_tls_PRF(ssl, + pms, pmslen, + (uchar_t *)TLS_MASTER_SECRET_LABEL, + (size_t)strlen(TLS_MASTER_SECRET_LABEL), + seed, sizeof (seed), + ssl->sid.master_secret, + (size_t)sizeof (ssl->sid.master_secret))); +} + +static void +kssl_generate_ssl_ms(ssl_t *ssl, uchar_t *pms, size_t pmslen) +{ + uchar_t buf[SSL3_PRE_MASTER_SECRET_LEN]; + uchar_t *ms; + int hlen = MD5_HASH_LEN; + + ms = ssl->sid.master_secret; + + /* if pms is bad fake it to thwart Bleichenbacher attack */ + if (pms == NULL || pmslen != SSL3_PRE_MASTER_SECRET_LEN || + pms[0] != ssl->major_version || pms[1] != ssl->minor_version) { +#ifdef DEBUG + cmn_err(CE_WARN, "Under Bleichenbacher attack"); +#endif /* DEBUG */ + KSSL_COUNTER(bad_pre_master_secret, 1); + pms = buf; + pmslen = SSL3_PRE_MASTER_SECRET_LEN; + pms[0] = ssl->major_version; + pms[1] = ssl->minor_version; + (void) random_get_pseudo_bytes(&buf[2], pmslen - 2); + } + kssl_ssl3_key_material_derive_step(ssl, pms, pmslen, 1, ms, 0); + kssl_ssl3_key_material_derive_step(ssl, pms, pmslen, 2, ms + hlen, 0); + kssl_ssl3_key_material_derive_step(ssl, pms, pmslen, 3, ms + 2 * hlen, + 0); +} + +static int +kssl_generate_tls_keyblock(ssl_t *ssl) +{ + uchar_t seed[2 * SSL3_RANDOM_LENGTH]; + + bcopy(ssl->server_random, seed, SSL3_RANDOM_LENGTH); + bcopy(ssl->client_random, seed + SSL3_RANDOM_LENGTH, + SSL3_RANDOM_LENGTH); + + return (kssl_tls_PRF(ssl, ssl->sid.master_secret, + (size_t)SSL3_MASTER_SECRET_LEN, + (uchar_t *)TLS_KEY_EXPANSION_LABEL, + (size_t)strlen(TLS_KEY_EXPANSION_LABEL), + seed, (size_t)sizeof (seed), + ssl->pending_keyblock, + (size_t)ssl->pending_keyblksz)); + +} + +static void +kssl_generate_keyblock(ssl_t *ssl) +{ + uchar_t *ms; + size_t mslen = SSL3_MASTER_SECRET_LEN; + int hlen = MD5_HASH_LEN; + uchar_t *keys = ssl->pending_keyblock; + int steps = howmany(ssl->pending_keyblksz, hlen); + int i; + + ms = ssl->sid.master_secret; + + ASSERT(hlen * steps <= MAX_KEYBLOCK_LENGTH); + + for (i = 1; i <= steps; i++) { + kssl_ssl3_key_material_derive_step(ssl, ms, mslen, i, keys, 1); + keys += hlen; + } +} + +static char *ssl3_key_derive_seeds[8] = {"A", "BB", "CCC", "DDDD", "EEEEE", + "FFFFFF", "GGGGGGG", "HHHHHHHH"}; + +static void +kssl_ssl3_key_material_derive_step( + ssl_t *ssl, + uchar_t *secret, + size_t secretlen, + int step, + uchar_t *dst, + int sr_first) +{ + SHA1_CTX sha1, *sha1ctx; + MD5_CTX md5, *md5ctx; + uchar_t sha1_hash[SHA1_HASH_LEN]; + + sha1ctx = &sha1; + md5ctx = &md5; + + ASSERT(step <= + sizeof (ssl3_key_derive_seeds) / + sizeof (ssl3_key_derive_seeds[0])); + step--; + + SHA1Init(sha1ctx); + SHA1Update(sha1ctx, (uchar_t *)ssl3_key_derive_seeds[step], + step + 1); + SHA1Update(sha1ctx, secret, secretlen); + if (sr_first) { + SHA1Update(sha1ctx, ssl->server_random, SSL3_RANDOM_LENGTH); + SHA1Update(sha1ctx, ssl->client_random, SSL3_RANDOM_LENGTH); + } else { + SHA1Update(sha1ctx, ssl->client_random, SSL3_RANDOM_LENGTH); + SHA1Update(sha1ctx, ssl->server_random, SSL3_RANDOM_LENGTH); + } + SHA1Final(sha1_hash, sha1ctx); + + MD5Init(md5ctx); + MD5Update(md5ctx, secret, secretlen); + MD5Update(md5ctx, sha1_hash, SHA1_HASH_LEN); + MD5Final(dst, md5ctx); +} + +static int +kssl_send_certificate_and_server_hello_done(ssl_t *ssl) +{ + int cur_reclen; + int mss; + int len, copylen; + mblk_t *mp; + uchar_t *cert_buf; + int cert_len; + uchar_t *msgbuf; + Certificate_t *cert; + + cert = ssl->kssl_entry->ke_server_certificate; + if (cert == NULL) { + return (ENOENT); + } + cert_buf = cert->msg; + cert_len = cert->len; + + mp = ssl->handshake_sendbuf; + mss = ssl->tcp_mss; + ASSERT(mp != NULL); + cur_reclen = mp->b_wptr - mp->b_rptr - SSL3_HDR_LEN; + ASSERT(cur_reclen == KSSL_SSL3_SH_RECLEN); + /* Assume MSS is at least 80 bytes */ + ASSERT(mss > cur_reclen + SSL3_HDR_LEN); + ASSERT(cur_reclen < SSL3_MAX_RECORD_LENGTH); /* XXX */ + + copylen = mss - (cur_reclen + SSL3_HDR_LEN); + len = cert_len; + copylen = MIN(copylen, len); + copylen = MIN(copylen, SSL3_MAX_RECORD_LENGTH - cur_reclen); + + /* new record always starts in a new mblk for simplicity */ + msgbuf = cert_buf; + for (;;) { + ASSERT(mp->b_wptr + copylen <= mp->b_datap->db_lim); + bcopy(msgbuf, mp->b_wptr, copylen); + msgbuf += copylen; + mp->b_wptr += copylen; + cur_reclen += copylen; + len -= copylen; + if (len == 0) { + break; + } + if (cur_reclen == SSL3_MAX_RECORD_LENGTH) { + cur_reclen = 0; + } + copylen = MIN(len, mss); + copylen = MIN(copylen, SSL3_MAX_RECORD_LENGTH - cur_reclen); + mp->b_cont = allocb(copylen, BPRI_HI); + if (mp->b_cont == NULL) { + KSSL_COUNTER(alloc_fails, 1); + freemsg(ssl->handshake_sendbuf); + ssl->handshake_sendbuf = NULL; + return (ENOMEM); + } + mp = mp->b_cont; + if (cur_reclen == 0) { + mp->b_wptr[0] = content_handshake; + mp->b_wptr[1] = ssl->major_version; + mp->b_wptr[2] = ssl->minor_version; + cur_reclen = MIN(len, SSL3_MAX_RECORD_LENGTH); + mp->b_wptr[3] = (cur_reclen >> 8) & 0xff; + mp->b_wptr[4] = (cur_reclen) & 0xff; + mp->b_wptr += SSL3_HDR_LEN; + cur_reclen = 0; + copylen = MIN(copylen, mss - SSL3_HDR_LEN); + } + } + + /* adjust the record length field for the first record */ + mp = ssl->handshake_sendbuf; + cur_reclen = MIN(KSSL_SSL3_SH_RECLEN + cert_len, + SSL3_MAX_RECORD_LENGTH); + mp->b_rptr[3] = (cur_reclen >> 8) & 0xff; + mp->b_rptr[4] = (cur_reclen) & 0xff; + + kssl_update_handshake_hashes(ssl, cert_buf, cert_len); + + return (0); +} + +static int +kssl_send_change_cipher_specs(ssl_t *ssl) +{ + mblk_t *mp, *newmp; + uchar_t *buf; + + mp = ssl->handshake_sendbuf; + + /* We're most likely to hit the fast path for resumed sessions */ + if ((mp != NULL) && + (mp->b_datap->db_lim - mp->b_wptr > KSSL_SSL3_MAX_CCP_FIN_MSGLEN)) { + buf = mp->b_wptr; + } else { + newmp = allocb(KSSL_SSL3_MAX_CCP_FIN_MSGLEN, BPRI_HI); + + if (newmp == NULL) + return (ENOMEM); /* need to do better job! */ + + if (mp == NULL) { + ssl->handshake_sendbuf = newmp; + } else { + linkb(ssl->handshake_sendbuf, newmp); + } + mp = newmp; + buf = mp->b_rptr; + } + + /* 5 byte record header */ + buf[0] = content_change_cipher_spec; + buf[1] = ssl->major_version; + buf[2] = ssl->minor_version; + buf[3] = 0; + buf[4] = 1; + buf += SSL3_HDR_LEN; + + buf[0] = 1; + + mp->b_wptr = buf + 1; + ASSERT(mp->b_wptr < mp->b_datap->db_lim); + + ssl->seq_num[KSSL_WRITE] = 0; + return (kssl_spec_init(ssl, KSSL_WRITE)); +} + +int +kssl_spec_init(ssl_t *ssl, int dir) +{ + KSSL_HASHCTX *ctx; + KSSLCipherSpec *spec = &ssl->spec[dir]; + int ret = 0; + + spec->mac_hashsz = mac_defs[ssl->pending_malg].hashsz; + spec->mac_padsz = mac_defs[ssl->pending_malg].padsz; + + spec->MAC_HashInit = mac_defs[ssl->pending_malg].HashInit; + spec->MAC_HashUpdate = mac_defs[ssl->pending_malg].HashUpdate; + spec->MAC_HashFinal = mac_defs[ssl->pending_malg].HashFinal; + + if (dir == KSSL_READ) { + bcopy(ssl->pending_keyblock, ssl->mac_secret[dir], + spec->mac_hashsz); + } else { + bcopy(&(ssl->pending_keyblock[spec->mac_hashsz]), + ssl->mac_secret[dir], spec->mac_hashsz); + } + + /* Pre-compute these here. will save cycles on each record later */ + if (!IS_TLS(ssl)) { + ctx = &ssl->mac_ctx[dir][0]; + spec->MAC_HashInit((void *)ctx); + spec->MAC_HashUpdate((void *)ctx, ssl->mac_secret[dir], + spec->mac_hashsz); + spec->MAC_HashUpdate((void *)ctx, kssl_pad_1, + spec->mac_padsz); + + ctx = &ssl->mac_ctx[dir][1]; + spec->MAC_HashInit((void *)ctx); + spec->MAC_HashUpdate((void *)ctx, ssl->mac_secret[dir], + spec->mac_hashsz); + spec->MAC_HashUpdate((void *)ctx, kssl_pad_2, + spec->mac_padsz); + } + + spec->cipher_type = cipher_defs[ssl->pending_calg].type; + spec->cipher_mech.cm_type = cipher_defs[ssl->pending_calg].mech_type; + spec->cipher_bsize = cipher_defs[ssl->pending_calg].bsize; + spec->cipher_keysz = cipher_defs[ssl->pending_calg].keysz; + + if (spec->cipher_ctx != NULL) { + crypto_cancel_ctx(spec->cipher_ctx); + spec->cipher_ctx = 0; + } + + /* + * Initialize HMAC keys for TLS. + */ + if (IS_TLS(ssl)) { + if (ssl->pending_malg == mac_md5) { + spec->hmac_mech = hmac_md5_mech; + } else if (ssl->pending_malg == mac_sha) { + spec->hmac_mech = hmac_sha1_mech; + } + + spec->hmac_key.ck_format = CRYPTO_KEY_RAW; + spec->hmac_key.ck_data = ssl->mac_secret[dir]; + spec->hmac_key.ck_length = spec->mac_hashsz * 8; + } + + /* We're done if this is the nil cipher */ + if (spec->cipher_keysz == 0) { + return (0); + } + + /* Initialize the key and the active context */ + spec->cipher_key.ck_format = CRYPTO_KEY_RAW; + spec->cipher_key.ck_length = 8 * spec->cipher_keysz; /* in bits */ + + if (cipher_defs[ssl->pending_calg].bsize > 0) { + /* client_write_IV */ + spec->cipher_mech.cm_param = + (caddr_t)&(ssl->pending_keyblock[2 * spec->mac_hashsz + + 2 * spec->cipher_keysz]); + spec->cipher_mech.cm_param_len = spec->cipher_bsize; + } + spec->cipher_data.cd_format = CRYPTO_DATA_RAW; + if (dir == KSSL_READ) { + spec->cipher_mech.cm_param_len = + cipher_defs[ssl->pending_calg].bsize; + + /* cilent_write_key */ + spec->cipher_key.ck_data = + &(ssl->pending_keyblock[2 * spec->mac_hashsz]); + + ret = crypto_decrypt_init(&(spec->cipher_mech), + &(spec->cipher_key), NULL, &spec->cipher_ctx, NULL); +#ifdef DEBUG + if (CRYPTO_ERR(ret)) { + cmn_err(CE_WARN, "kssl_spec_init: " + "crypto_decrypt_init error 0x%02X", ret); + } +#endif /* DEBUG */ + } else { + if (cipher_defs[ssl->pending_calg].bsize > 0) { + spec->cipher_mech.cm_param += spec->cipher_bsize; + } + /* server_write_key */ + spec->cipher_key.ck_data = + &(ssl->pending_keyblock[2 * spec->mac_hashsz + + spec->cipher_keysz]); + + ret = crypto_encrypt_init(&(spec->cipher_mech), + &(spec->cipher_key), NULL, &spec->cipher_ctx, NULL); +#ifdef DEBUG + if (CRYPTO_ERR(ret)) + cmn_err(CE_WARN, "kssl_spec_init: " + "crypto_encrypt_init error 0x%02X", ret); +#endif /* DEBUG */ + } + return (ret); +} + +static int +kssl_send_finished(ssl_t *ssl, int update_hsh) +{ + mblk_t *mp; + uchar_t *buf; + uchar_t *rstart; + uchar_t *versionp; + SSL3Hashes ssl3hashes; + size_t finish_len; + int ret = 0; + + mp = ssl->handshake_sendbuf; + ASSERT(mp != NULL); + buf = mp->b_wptr; + ASSERT(buf - mp->b_rptr == SSL3_HDR_LEN + KSSL_SSL3_SH_RECLEN + + SSL3_HDR_LEN + 1 || buf - mp->b_rptr == SSL3_HDR_LEN + 1); + + rstart = buf; + + if (IS_TLS(ssl)) + finish_len = TLS_FINISHED_SIZE; + else + finish_len = KSSL_SSL3_FIN_MSGLEN; + + /* 5 byte record header */ + buf[0] = content_handshake; + buf[1] = ssl->major_version; + buf[2] = ssl->minor_version; + buf[3] = 0; + buf[4] = 4 + finish_len; + + versionp = &buf[1]; + + buf += SSL3_HDR_LEN; + + /* 4 byte message header */ + buf[0] = (uchar_t)finished; /* message type */ + buf[1] = 0; /* message len byte 0 */ + buf[2] = 0; /* message len byte 1 */ + buf[3] = finish_len; /* message len byte 2 */ + buf += 4; + + if (IS_TLS(ssl)) { + bcopy(ssl->hs_hashes.md5, ssl3hashes.md5, + sizeof (ssl3hashes.md5)); + bcopy(ssl->hs_hashes.sha1, ssl3hashes.sha1, + sizeof (ssl3hashes.sha1)); + } + + /* Compute hashes for the SENDER side */ + ret = kssl_compute_handshake_hashes(ssl, &ssl3hashes, sender_server); + if (ret != 0) + return (ret); + + if (IS_TLS(ssl)) { + bcopy(ssl3hashes.tlshash, buf, sizeof (ssl3hashes.tlshash)); + } else { + bcopy(ssl3hashes.md5, buf, MD5_HASH_LEN); + bcopy(ssl3hashes.sha1, buf + MD5_HASH_LEN, SHA1_HASH_LEN); + } + + if (update_hsh) { + kssl_update_handshake_hashes(ssl, buf - 4, finish_len + 4); + } + + mp->b_wptr = buf + finish_len; + + ret = kssl_mac_encrypt_record(ssl, content_handshake, versionp, + rstart, mp); + ASSERT(mp->b_wptr <= mp->b_datap->db_lim); + + return (ret); +} + +int +kssl_mac_encrypt_record(ssl_t *ssl, + SSL3ContentType ct, + uchar_t *versionp, + uchar_t *rstart, + mblk_t *mp) +{ + KSSLCipherSpec *spec; + int mac_sz; + int ret = 0; + uint16_t rec_sz; + int pad_sz; + int i; + + ASSERT(ssl != NULL); + ASSERT(rstart >= mp->b_rptr); + ASSERT(rstart < mp->b_wptr); + + spec = &ssl->spec[KSSL_WRITE]; + mac_sz = spec->mac_hashsz; + + rec_sz = (mp->b_wptr - rstart) - SSL3_HDR_LEN; + ASSERT(rec_sz > 0); + + if (mac_sz != 0) { + ASSERT(mp->b_wptr + mac_sz <= mp->b_datap->db_lim); + ret = kssl_compute_record_mac(ssl, KSSL_WRITE, + ssl->seq_num[KSSL_WRITE], ct, versionp, + rstart + SSL3_HDR_LEN, rec_sz, mp->b_wptr); + if (ret == CRYPTO_SUCCESS) { + ssl->seq_num[KSSL_WRITE]++; + mp->b_wptr += mac_sz; + rec_sz += mac_sz; + } else { + return (ret); + } + } + + if (spec->cipher_type == type_block) { + pad_sz = spec->cipher_bsize - + (rec_sz & (spec->cipher_bsize - 1)); + ASSERT(mp->b_wptr + pad_sz <= mp->b_datap->db_lim); + for (i = 0; i < pad_sz; i++) { + mp->b_wptr[i] = pad_sz - 1; + } + mp->b_wptr += pad_sz; + rec_sz += pad_sz; + } + + ASSERT(rec_sz <= SSL3_MAX_RECORD_LENGTH); + + U16_TO_BE16(rec_sz, rstart + 3); + + if (spec->cipher_ctx == 0) + return (ret); + + spec->cipher_data.cd_length = rec_sz; + spec->cipher_data.cd_raw.iov_base = (char *)(rstart + SSL3_HDR_LEN); + spec->cipher_data.cd_raw.iov_len = rec_sz; + /* One record at a time. Otherwise, gotta allocate the crypt_data_t */ + ret = crypto_encrypt_update(spec->cipher_ctx, &spec->cipher_data, + NULL, NULL); +#ifdef DEBUG + if (CRYPTO_ERR(ret)) { + cmn_err(CE_WARN, + "kssl_mac_encrypt_record: crypto_encrypt_update " + "error 0x%02X", ret); + } +#endif /* DEBUG */ + return (ret); +} + +void +kssl_send_alert(ssl_t *ssl, SSL3AlertLevel level, SSL3AlertDescription desc) +{ + mblk_t *mp; + uchar_t *buf; + KSSLCipherSpec *spec; + + ASSERT(ssl != NULL); + + ssl->sendalert_level = level; + ssl->sendalert_desc = desc; + + if (level == alert_fatal) { +#ifdef DEBUG + cmn_err(CE_WARN, "sending an alert %d %d from %p\n", level, + desc, (void *)caller()); +#endif /* DEBUG */ + if (ssl->sid.cached == B_TRUE) { + kssl_uncache_sid(&ssl->sid, ssl->kssl_entry); + ssl->sid.cached = B_FALSE; + } + ssl->fatal_alert = B_TRUE; + KSSL_COUNTER(fatal_alerts, 1); + } else + KSSL_COUNTER(warning_alerts, 1); + + spec = &ssl->spec[KSSL_WRITE]; + + ASSERT(ssl->alert_sendbuf == NULL); + ssl->alert_sendbuf = mp = allocb(7 + spec->mac_hashsz + + spec->cipher_bsize, BPRI_HI); + if (mp == NULL) { + KSSL_COUNTER(alloc_fails, 1); + return; + } + buf = mp->b_wptr; + + /* 5 byte record header */ + buf[0] = content_alert; + buf[1] = ssl->major_version; + buf[2] = ssl->minor_version; + buf[3] = 0; + buf[4] = 2; + buf += SSL3_HDR_LEN; + + /* alert contents */ + buf[0] = (uchar_t)level; + buf[1] = (uchar_t)desc; + + mp->b_wptr = buf + 2; +} + +/* Assumes RSA encryption */ +static int +kssl_handle_client_key_exchange(ssl_t *ssl, mblk_t *mp, int msglen, + kssl_callback_t cbfn, void *arg) +{ + char *buf; + uchar_t *pms; + size_t pmslen; + int allocated; + int err; + crypto_key_t *privkey; + crypto_data_t *wrapped_pms_data, *pms_data; + crypto_call_req_t creq, *creqp; + + privkey = ssl->kssl_entry->ke_private_key; + if (privkey == NULL) { + return (ENOENT); + } + + ASSERT(ssl->msg.type == client_key_exchange); + ASSERT(ssl->hs_waitstate == wait_client_key); + + /* + * TLS adds an extra 2 byte length field before the data. + */ + if (IS_TLS(ssl)) { + msglen = (mp->b_rptr[0] << 8) | mp->b_rptr[1]; + mp->b_rptr += 2; + } + + /* + * Allocate all we need in one shot. about 300 bytes total, for + * 1024 bit RSA modulus. + * The buffer layout will be: pms_data, wrapped_pms_data, the + * value of the wrapped pms from the client, then room for the + * resulting decrypted premaster secret. + */ + allocated = 2 * (sizeof (crypto_data_t) + msglen); + buf = kmem_alloc(allocated, KM_NOSLEEP); + if (buf == NULL) { + return (ENOMEM); + } + + pms_data = (crypto_data_t *)buf; + wrapped_pms_data = &(((crypto_data_t *)buf)[1]); + + wrapped_pms_data->cd_format = pms_data->cd_format = CRYPTO_DATA_RAW; + wrapped_pms_data->cd_offset = pms_data->cd_offset = 0; + wrapped_pms_data->cd_length = pms_data->cd_length = msglen; + wrapped_pms_data->cd_miscdata = pms_data->cd_miscdata = NULL; + wrapped_pms_data->cd_raw.iov_len = pms_data->cd_raw.iov_len = msglen; + wrapped_pms_data->cd_raw.iov_base = buf + 2 * sizeof (crypto_data_t); + pms_data->cd_raw.iov_base = wrapped_pms_data->cd_raw.iov_base + msglen; + + bcopy(mp->b_rptr, wrapped_pms_data->cd_raw.iov_base, msglen); + mp->b_rptr += msglen; + + /* Proceed synchronously if out of interrupt and configured to do so */ + if ((kssl_synchronous) && (!servicing_interrupt())) { + creqp = NULL; + } else { + ssl->cke_callback_func = cbfn; + ssl->cke_callback_arg = arg; + creq.cr_flag = kssl_call_flag; + creq.cr_callback_func = kssl_cke_done; + creq.cr_callback_arg = ssl; + + /* The callback routine will release this one */ + KSSL_SSL_REFHOLD(ssl); + + creqp = &creq; + } + + err = crypto_decrypt(&rsa_x509_mech, wrapped_pms_data, + privkey, NULL, pms_data, creqp); + + switch (err) { + case CRYPTO_SUCCESS: + break; + + case CRYPTO_QUEUED: + /* + * Finish the master secret then the rest of key material + * derivation later. + */ + ssl->job.kjob = creq.cr_reqid; + ssl->job.buf = buf; + ssl->job.buflen = allocated; + ssl->hs_waitstate = wait_client_key_done; + return (0); + default: +#ifdef DEBUG + cmn_err(CE_WARN, "kssl_handle_client_key_exchange: " + "crypto_decrypt error 0x%02X", err); +#endif /* DEBUG */ + kmem_free(buf, allocated); + return (ENOMEM); + } + + pmslen = pms_data->cd_length; + pms = kssl_rsa_unwrap((uchar_t *)pms_data->cd_raw.iov_base, &pmslen); + + /* generate master key and save it in the ssl sid structure */ + if (IS_TLS(ssl)) { + err = kssl_generate_tls_ms(ssl, pms, pmslen); + if (!CRYPTO_ERR(err)) + err = kssl_generate_tls_keyblock(ssl); + } else { + kssl_generate_ssl_ms(ssl, pms, pmslen); + kssl_generate_keyblock(ssl); + } + + if (err == CRYPTO_SUCCESS) + ssl->hs_waitstate = wait_change_cipher; + + ssl->activeinput = B_FALSE; + + kmem_free(buf, allocated); + + return (0); +} + +static int +kssl_handle_finished(ssl_t *ssl, mblk_t *mp, int msglen) +{ + int err; + size_t finish_len; + int hashcompare; + + ASSERT(ssl->msg.type == finished); + ASSERT(ssl->hs_waitstate == wait_finished); + + if (IS_TLS(ssl)) + finish_len = TLS_FINISHED_SIZE; + else + finish_len = KSSL_SSL3_FIN_MSGLEN; + + if (msglen != finish_len) { + kssl_send_alert(ssl, alert_fatal, illegal_parameter); + return (EBADMSG); + } + + if (IS_TLS(ssl)) { + hashcompare = bcmp(mp->b_rptr, ssl->hs_hashes.tlshash, + finish_len); + } else { + hashcompare = bcmp(mp->b_rptr, &ssl->hs_hashes, finish_len); + } + + /* The handshake hashes should be computed by now */ + if (hashcompare != 0) { + kssl_send_alert(ssl, alert_fatal, handshake_failure); + return (EBADMSG); + } + + mp->b_rptr += msglen; + + ssl->hs_waitstate = idle_handshake; + + if (ssl->resumed == B_TRUE) { + ssl->activeinput = B_FALSE; + return (0); + } + + err = kssl_send_change_cipher_specs(ssl); + if (err != 0) { + return (err); + } + err = kssl_send_finished(ssl, 0); + if (err != 0) { + return (err); + } + + ASSERT(ssl->sid.cached == B_FALSE); + ssl->sid.cached = B_TRUE; + kssl_cache_sid(&ssl->sid, ssl->kssl_entry); + ssl->activeinput = B_FALSE; + + return (0); +} + +#define KSSL2_CH_MIN_RECSZ (9) + +/* + * This method is needed to handle clients which send the + * SSLv2/SSLv3 handshake for backwards compat with SSLv2 servers. + * We are not really doing SSLv2 here, just handling the header + * and then switching to SSLv3. + */ +int +kssl_handle_v2client_hello(ssl_t *ssl, mblk_t *mp, int recsz) +{ + uchar_t *recend; + int err; + SSL3AlertDescription desc = illegal_parameter; + uint_t randlen; + uint_t sidlen; + uint_t nsuites; + uchar_t *suitesp; + uchar_t *rand; + uint_t i, j; + uint16_t suite; + int ch_recsz = KSSL2_CH_MIN_RECSZ; + + ASSERT(mp->b_wptr >= mp->b_rptr + recsz); + ASSERT(ssl->hs_waitstate == wait_client_hello); + ASSERT(ssl->resumed == B_FALSE); + + if (recsz < ch_recsz) { + goto falert; + } + + MD5Init(&ssl->hs_md5); + SHA1Init(&ssl->hs_sha1); + + kssl_update_handshake_hashes(ssl, mp->b_rptr, recsz); + + recend = mp->b_rptr + recsz; + + if (*mp->b_rptr != 1) { + goto falert; + } + mp->b_rptr += 3; + + nsuites = ((uint_t)mp->b_rptr[0] << 8) + (uint_t)mp->b_rptr[1]; + sidlen = ((uint_t)mp->b_rptr[2] << 8) + (uint_t)mp->b_rptr[3]; + randlen = ((uint_t)mp->b_rptr[4] << 8) + (uint_t)mp->b_rptr[5]; + if (nsuites % 3 != 0) { + KSSL_DEBUG2_IF(kssl_debug, + "kssl_handle_v2client_hello nsuites = %d, error.", + nsuites); + goto falert; + } + if (randlen < SSL_MIN_CHALLENGE_BYTES || + randlen > SSL_MAX_CHALLENGE_BYTES) { + KSSL_DEBUG2_IF(kssl_debug, + "kssl_handle_v2client_hello randlen out of range: %d", + randlen); + goto falert; + } + mp->b_rptr += 6; + ch_recsz += nsuites + sidlen + randlen; + if (recsz != ch_recsz) { + goto falert; + } + suitesp = mp->b_rptr; + rand = suitesp + nsuites + sidlen; + if (randlen < SSL3_RANDOM_LENGTH) { + bzero(ssl->client_random, SSL3_RANDOM_LENGTH); + } + bcopy(rand, &ssl->client_random[SSL3_RANDOM_LENGTH - randlen], + randlen); + + for (i = 0; i < ssl->kssl_entry->kssl_cipherSuites_nentries; i++) { + suite = ssl->kssl_entry->kssl_cipherSuites[i]; + for (j = 0; j < nsuites; j += 3) { + if (suitesp[j] != 0) { + continue; + } + + if (suitesp[j + 1] == ((suite >> 8) & 0xff) && + suitesp[j + 2] == (suite & 0xff)) { + break; + } + } + if (j < nsuites) { + break; + } + } + if (i == ssl->kssl_entry->kssl_cipherSuites_nentries) { + KSSL_DEBUG1_IF(kssl_debug, "kssl_handle_v2client_hello - " + "cannot find SSLv2 cipher suite"); + ssl->activeinput = B_FALSE; + return (SSL_MISS); + } + + mp->b_rptr = recend; + + for (i = 0; i < cipher_suite_defs_nentries; i++) { + if (suite == cipher_suite_defs[i].suite) { + break; + } + } + + ASSERT(i < cipher_suite_defs_nentries); + + ssl->pending_cipher_suite = suite; + ssl->pending_malg = cipher_suite_defs[i].malg; + ssl->pending_calg = cipher_suite_defs[i].calg; + ssl->pending_keyblksz = cipher_suite_defs[i].keyblksz; + + ASSERT(ssl->sid.cached == B_FALSE); + + (void) random_get_pseudo_bytes(ssl->sid.session_id, + SSL3_SESSIONID_BYTES); + ssl->sid.client_addr = ssl->faddr; + ssl->sid.cipher_suite = suite; + + err = kssl_send_server_hello(ssl); + if (err != 0) { + return (err); + } + err = kssl_send_certificate_and_server_hello_done(ssl); + if (err != 0) { + return (err); + } + KSSL_COUNTER(full_handshakes, 1); + ssl->hs_waitstate = wait_client_key; + ssl->activeinput = B_FALSE; + return (0); + +falert: + kssl_send_alert(ssl, alert_fatal, desc); + ssl->activeinput = B_FALSE; + return (EBADMSG); +} + +/* + * Call back routine for asynchronously submitted RSA decryption jobs. + * The routine retreived the pre-master secret, and proceeds to generate + * the remaining key materials. + */ +static void +kssl_cke_done(void *arg, int status) +{ + int ret = 0; + uchar_t *pms; + size_t pmslen; + crypto_data_t *pms_data; + kssl_cmd_t kssl_cmd = KSSL_CMD_NONE; + ssl_t *ssl = (ssl_t *)arg; + mblk_t *alertmp; + kssl_callback_t cbfn; + void *cbarg; + + mutex_enter(&ssl->kssl_lock); + + ASSERT(ssl->msg.type == client_key_exchange); + ASSERT(ssl->hs_waitstate == wait_client_key_done); + + if (status != CRYPTO_SUCCESS) { + kssl_send_alert(ssl, alert_fatal, decrypt_error); + kssl_cmd = KSSL_CMD_SEND; + goto out; + } + + pms_data = (crypto_data_t *)(ssl->job.buf); + + ASSERT(pms_data != NULL); + + pmslen = pms_data->cd_length; + pms = kssl_rsa_unwrap((uchar_t *)pms_data->cd_raw.iov_base, &pmslen); + + /* generate master key and save it in the ssl sid structure */ + if (IS_TLS(ssl)) { + ret = kssl_generate_tls_ms(ssl, pms, pmslen); + if (!CRYPTO_ERR(ret)) + ret = kssl_generate_tls_keyblock(ssl); + } else { + kssl_generate_ssl_ms(ssl, pms, pmslen); + kssl_generate_keyblock(ssl); + } + + if (ret == CRYPTO_SUCCESS) + ssl->hs_waitstate = wait_change_cipher; + +out: + kmem_free(ssl->job.buf, ssl->job.buflen); + + ssl->job.kjob = 0; + ssl->job.buf = NULL; + ssl->job.buflen = 0; + + ssl->activeinput = B_FALSE; + + /* If we're the only ones left, then we won't callback */ + if (ssl->kssl_refcnt == 1) { + mutex_exit(&ssl->kssl_lock); + KSSL_SSL_REFRELE(ssl); + return; + } + + cbfn = ssl->cke_callback_func; + cbarg = ssl->cke_callback_arg; + alertmp = ssl->alert_sendbuf; + ssl->alert_sendbuf = NULL; + + mutex_exit(&ssl->kssl_lock); + + KSSL_SSL_REFRELE(ssl); + + /* Now call the callback routine */ + (*(cbfn))(cbarg, alertmp, kssl_cmd); +} + +/* + * Returns the first complete contiguous record out of rec_ass_head + * The record is returned in a separate contiguous mblk, rec_ass_head is + * left pointing to the next record in the queue. + * + * The output looks as follows: + * + * |--------|---------- .... -----|<---------->|<----------->|--- ... ---| + * ^ ^ ^ mac_size pad_size ^ + * | |___ b_rptr b_wptr __| | + * | | + * |___ db_base db_lim ___| + */ +mblk_t * +kssl_get_next_record(ssl_t *ssl) +{ + mblk_t *mp, *retmp; + int rhsz = SSL3_HDR_LEN; + uint16_t rec_sz; + int mpsz, total_size; + SSL3ContentType content_type; + + ASSERT(MUTEX_HELD(&ssl->kssl_lock)); + + mp = ssl->rec_ass_head; + if (mp == NULL) + return (NULL); + + /* Fast path: when mp has at least a complete record */ + if (MBLKL(mp) < rhsz) { + /* Not even a complete header in there yet */ + if (msgdsize(mp) < rhsz) { + return (NULL); + } + + if (!pullupmsg(mp, rhsz)) { + kssl_send_alert(ssl, alert_fatal, internal_error); + freemsg(mp); + ssl->rec_ass_head = ssl->rec_ass_tail = NULL; + return (NULL); + } + } + content_type = (SSL3ContentType)mp->b_rptr[0]; + if (content_type == content_handshake_v2) { + rec_sz = (uint16_t)mp->b_rptr[1]; + rhsz = 2; + } else { + uint8_t *rec_sz_p = (uint8_t *)mp->b_rptr + 3; + rec_sz = BE16_TO_U16(rec_sz_p); + } + + /* + * same tests as above. Only rare very fragmented cases will + * incur the cost of msgdsize() and msgpullup(). Well formed + * packets will fall in the most frequent fast path. + */ + total_size = rhsz + rec_sz; + + /* + * Missing: defensive against record fabricated with longer than + * MAX record length. + */ + if (MBLKL(mp) < total_size) { + /* Not a complete record yet. Keep accumulating */ + if (msgdsize(mp) < total_size) { + return (NULL); + } + + if (!pullupmsg(mp, total_size)) { + kssl_send_alert(ssl, alert_fatal, internal_error); + freemsg(mp); + ssl->rec_ass_head = ssl->rec_ass_tail = NULL; + return (NULL); + } + } + mpsz = MBLKL(mp); /* could've changed after the pullup */ + + if (mpsz > total_size) { + /* gotta allocate a new block */ + if ((retmp = dupb(mp)) == NULL) { + kssl_send_alert(ssl, alert_fatal, internal_error); + freemsg(mp); + ssl->rec_ass_head = ssl->rec_ass_tail = NULL; + return (NULL); + } + + retmp->b_wptr = retmp->b_rptr + total_size; + mp->b_rptr += total_size; + ssl->rec_ass_head = mp; + } else { + ASSERT(mpsz == total_size); + ssl->rec_ass_head = mp->b_cont; + mp->b_cont = NULL; + retmp = mp; + } + /* Adjust the tail */ + if ((mp = ssl->rec_ass_tail = ssl->rec_ass_head) != NULL) { + for (; mp->b_cont != NULL; mp = mp->b_cont) { + ssl->rec_ass_tail = mp->b_cont; + } + } + + return (retmp); +} + + +static void +kssl_mblksfree(ssl_t *ssl) +{ + + ASSERT(ssl != NULL); + + if (ssl->rec_ass_head != NULL) { + freemsg(ssl->rec_ass_head); + } + ssl->rec_ass_head = NULL; + ssl->rec_ass_tail = NULL; + + if (ssl->msg.head != NULL) { + freemsg(ssl->msg.head); + } + ssl->msg.head = NULL; + ssl->msg.tail = NULL; + + if (ssl->handshake_sendbuf != NULL) { + freemsg(ssl->handshake_sendbuf); + ssl->handshake_sendbuf = NULL; + } + if (ssl->alert_sendbuf != NULL) { + freemsg(ssl->alert_sendbuf); + ssl->alert_sendbuf = NULL; + } +} + +static void +kssl_specsfree(ssl_t *ssl) +{ + KSSLCipherSpec *spec = &ssl->spec[KSSL_READ]; + + if (spec->cipher_ctx != NULL) { + crypto_cancel_ctx(spec->cipher_ctx); + spec->cipher_ctx = 0; + } + + spec = &ssl->spec[KSSL_WRITE]; + + if (spec->cipher_ctx != NULL) { + crypto_cancel_ctx(spec->cipher_ctx); + spec->cipher_ctx = 0; + } +} + +/* + * Frees the ssl structure (aka the context of an SSL session). + * Any pending crypto jobs are cancelled. + * Any initiated crypto contexts are freed as well. + */ +void +kssl_free_context(ssl_t *ssl) +{ + ASSERT(ssl != NULL); + if (!(MUTEX_HELD(&ssl->kssl_lock))) { + /* we're coming from an external API entry point */ + mutex_enter(&ssl->kssl_lock); + } + + if (ssl->job.kjob != NULL) { + crypto_cancel_req(ssl->job.kjob); + kmem_free(ssl->job.buf, ssl->job.buflen); + + ssl->job.kjob = 0; + ssl->job.buf = NULL; + ssl->job.buflen = 0; + } + + kssl_mblksfree(ssl); + kssl_specsfree(ssl); + + KSSL_ENTRY_REFRELE(ssl->kssl_entry); + ssl->kssl_entry = NULL; + + mutex_exit(&ssl->kssl_lock); + + kmem_cache_free(kssl_cache, ssl); + kssl_cache_count--; +} diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/common/inet/tcp.h --- a/usr/src/uts/common/inet/tcp.h Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/uts/common/inet/tcp.h Sat Nov 12 18:58:05 2005 -0800 @@ -40,6 +40,7 @@ #include #include #include +#include /* * Private (and possibly temporary) ioctl used by configuration code @@ -543,6 +544,13 @@ boolean_t tcp_issocket; /* this is a socket tcp */ uint32_t tcp_squeue_bytes; + /* + * Kernel SSL session information + */ + boolean_t tcp_kssl_pending; /* waiting for 1st SSL rec. */ + boolean_t tcp_kssl_inhandshake; /* during SSL handshake */ + kssl_ent_t tcp_kssl_ent; /* SSL table entry */ + kssl_ctx_t tcp_kssl_ctx; /* SSL session */ } tcp_t; extern void tcp_free(tcp_t *tcp); diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/common/inet/tcp/tcp.c --- a/usr/src/uts/common/inet/tcp/tcp.c Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/uts/common/inet/tcp/tcp.c Sat Nov 12 18:58:05 2005 -0800 @@ -94,6 +94,7 @@ #include #include #include +#include /* * TCP Notes: aka FireEngine Phase I (PSARC 2002/433) @@ -759,7 +760,7 @@ void tcp_input(void *arg, mblk_t *mp, void *arg2); void tcp_rput_data(void *arg, mblk_t *mp, void *arg2); static void tcp_close_output(void *arg, mblk_t *mp, void *arg2); -static void tcp_output(void *arg, mblk_t *mp, void *arg2); +void tcp_output(void *arg, mblk_t *mp, void *arg2); static void tcp_rsrv_input(void *arg, mblk_t *mp, void *arg2); static void tcp_timer_handler(void *arg, mblk_t *mp, void *arg2); @@ -980,6 +981,8 @@ static mblk_t *tcp_zcopy_backoff(tcp_t *, mblk_t *, int); static void tcp_ire_ill_check(tcp_t *, ire_t *, ill_t *, boolean_t); +extern void tcp_kssl_input(tcp_t *, mblk_t *); + /* * Routines related to the TCP_IOC_ABORT_CONN ioctl command. * @@ -1111,7 +1114,7 @@ /* TCP bind hash list - all tcp_t with state >= BOUND. */ -static tf_t tcp_bind_fanout[TCP_BIND_FANOUT_SIZE]; +tf_t tcp_bind_fanout[TCP_BIND_FANOUT_SIZE]; /* TCP queue hash list - all tcp_t in case they will be an acceptor. */ static tf_t tcp_acceptor_fanout[TCP_FANOUT_SIZE]; @@ -1676,6 +1679,18 @@ tcp_bind_hash_remove(tcp); tcp_free(tcp); + /* Release any SSL context */ + if (tcp->tcp_kssl_ent != NULL) { + kssl_release_ent(tcp->tcp_kssl_ent, NULL, KSSL_NO_PROXY); + tcp->tcp_kssl_ent = NULL; + } + + if (tcp->tcp_kssl_ctx != NULL) { + kssl_release_ctx(tcp->tcp_kssl_ctx); + tcp->tcp_kssl_ctx = NULL; + } + tcp->tcp_kssl_pending = B_FALSE; + conn_delete_ire(connp, NULL); if (connp->conn_flags & IPCL_TCPCONN) { if (connp->conn_latch != NULL) @@ -3713,6 +3728,7 @@ tcp_close_output, connp, SQTAG_IP_TCP_CLOSE); mutex_enter(&tcp->tcp_closelock); + while (!tcp->tcp_closed) cv_wait(&tcp->tcp_closecv, &tcp->tcp_closelock); mutex_exit(&tcp->tcp_closelock); @@ -3996,6 +4012,7 @@ tcp->tcp_rq = tcp_g_q; tcp->tcp_wq = WR(tcp_g_q); } + /* Signal tcp_close() to finish closing. */ tcp->tcp_closed = 1; cv_signal(&tcp->tcp_closecv); @@ -4086,6 +4103,7 @@ tcp->tcp_ibsegs = 0; UPDATE_MIB(&tcp_mib, tcpOutSegs, tcp->tcp_obsegs); tcp->tcp_obsegs = 0; + /* * If we are an eager connection hanging off a listener that * hasn't formally accepted the connection yet, get off his @@ -4164,6 +4182,17 @@ ASSERT(tcp->tcp_time_wait_prev == NULL); ASSERT(tcp->tcp_time_wait_expire == 0); tcp->tcp_state = TCPS_CLOSED; + + /* Release any SSL context */ + if (tcp->tcp_kssl_ent != NULL) { + kssl_release_ent(tcp->tcp_kssl_ent, NULL, KSSL_NO_PROXY); + tcp->tcp_kssl_ent = NULL; + } + if (tcp->tcp_kssl_ctx != NULL) { + kssl_release_ctx(tcp->tcp_kssl_ctx); + tcp->tcp_kssl_ctx = NULL; + } + tcp->tcp_kssl_pending = B_FALSE; } /* @@ -4691,6 +4720,13 @@ } tcp->tcp_conn.tcp_eager_conn_ind = tpi_mp; + /* Inherit the listener's SSL protection state */ + + if ((tcp->tcp_kssl_ent = ltcp->tcp_kssl_ent) != NULL) { + kssl_hold_ent(tcp->tcp_kssl_ent); + tcp->tcp_kssl_pending = B_TRUE; + } + return (0); } @@ -4806,6 +4842,12 @@ } tcp->tcp_conn.tcp_eager_conn_ind = tpi_mp; + /* Inherit the listener's SSL protection state */ + if ((tcp->tcp_kssl_ent = ltcp->tcp_kssl_ent) != NULL) { + kssl_hold_ent(tcp->tcp_kssl_ent); + tcp->tcp_kssl_pending = B_TRUE; + } + return (0); } @@ -7170,6 +7212,20 @@ ASSERT(tcp->tcp_time_wait_prev == NULL); ASSERT(tcp->tcp_time_wait_expire == 0); + if (tcp->tcp_kssl_pending) { + tcp->tcp_kssl_pending = B_FALSE; + + /* Don't reset if the initialized by bind. */ + if (tcp->tcp_kssl_ent != NULL) { + kssl_release_ent(tcp->tcp_kssl_ent, NULL, + KSSL_NO_PROXY); + } + } + if (tcp->tcp_kssl_ctx != NULL) { + kssl_release_ctx(tcp->tcp_kssl_ctx); + tcp->tcp_kssl_ctx = NULL; + } + /* * Reset/preserve other values */ @@ -7528,6 +7584,10 @@ PRESERVE(tcp->tcp_squeue_bytes); + ASSERT(tcp->tcp_kssl_ctx == NULL); + ASSERT(!tcp->tcp_kssl_pending); + PRESERVE(tcp->tcp_kssl_ent); + #undef DONTCARE #undef PRESERVE } @@ -8663,7 +8723,10 @@ * chunks. We round up the buffer size to the nearest SMSS. */ maxpsz = MSS_ROUNDUP(tcp->tcp_xmit_hiwater, mss); - mss = INFPSZ; + if (tcp->tcp_kssl_ctx == NULL) + mss = INFPSZ; + else + mss = SSL3_MAX_RECORD_LEN; } else { /* * Set sd_qn_maxpsz to approx half the (receivers) buffer @@ -11003,6 +11066,11 @@ #ifdef DEBUG cnt += msgdsize(mp); #endif + /* Does this need SSL processing first? */ + if ((tcp->tcp_kssl_ctx != NULL) && (DB_TYPE(mp) == M_DATA)) { + tcp_kssl_input(tcp, mp); + continue; + } putnext(q, mp); } ASSERT(cnt == tcp->tcp_rcv_cnt); @@ -12532,7 +12600,22 @@ BUMP_MIB(&tcp_mib, tcpInClosed); TCP_RECORD_TRACE(tcp, mp, TCP_TRACE_RECV_PKT); + freemsg(mp); + /* + * This could be an SSL closure alert. We're detached so just + * acknowledge it this last time. + */ + if (tcp->tcp_kssl_ctx != NULL) { + kssl_release_ctx(tcp->tcp_kssl_ctx); + tcp->tcp_kssl_ctx = NULL; + + tcp->tcp_rnxt += seg_len; + U32_TO_ABE32(tcp->tcp_rnxt, tcp->tcp_tcph->th_ack); + flags |= TH_ACK_NEEDED; + goto ack_check; + } + tcp_xmit_ctl("new data when detached", tcp, tcp->tcp_snxt, 0, TH_RST); (void) tcp_clean_death(tcp, EPROTO, 12); @@ -13241,7 +13324,8 @@ if (tcp->tcp_ipversion == IPV6_VERSION && bytes_acked > 0) tcp->tcp_ip_forward_progress = B_TRUE; if (tcp->tcp_state == TCPS_SYN_RCVD) { - if (tcp->tcp_conn.tcp_eager_conn_ind != NULL) { + if ((tcp->tcp_conn.tcp_eager_conn_ind != NULL) && + ((tcp->tcp_kssl_ent == NULL) || !tcp->tcp_kssl_pending)) { /* 3-way handshake complete - pass up the T_CONN_IND */ tcp_t *listener = tcp->tcp_listener; mblk_t *mp = tcp->tcp_conn.tcp_eager_conn_ind; @@ -13968,6 +14052,7 @@ } est: if (tcp->tcp_state > TCPS_ESTABLISHED) { + switch (tcp->tcp_state) { case TCPS_FIN_WAIT_1: if (tcp->tcp_fin_acked) { @@ -14170,7 +14255,12 @@ * Removing tcp_listener check for TH_URG * Making M_PCPROTO and MARK messages skip the eager case */ - tcp_rcv_enqueue(tcp, mp, seg_len); + + if (tcp->tcp_kssl_pending) { + tcp_kssl_input(tcp, mp); + } else { + tcp_rcv_enqueue(tcp, mp, seg_len); + } } else { if (mp->b_datap->db_type != M_DATA || (flags & TH_MARKNEXT_NEEDED)) { @@ -14189,9 +14279,16 @@ mp->b_flag |= MSGMARKNEXT; flags &= ~TH_MARKNEXT_NEEDED; } - putnext(tcp->tcp_rq, mp); - if (!canputnext(tcp->tcp_rq)) - tcp->tcp_rwnd -= seg_len; + + /* Does this need SSL processing first? */ + if ((tcp->tcp_kssl_ctx != NULL) && + (DB_TYPE(mp) == M_DATA)) { + tcp_kssl_input(tcp, mp); + } else { + putnext(tcp->tcp_rq, mp); + if (!canputnext(tcp->tcp_rq)) + tcp->tcp_rwnd -= seg_len; + } } else if (((flags & (TH_PUSH|TH_FIN)) || tcp->tcp_rcv_cnt + seg_len >= tcp->tcp_rq->q_hiwat >> 3) && (sqp != NULL)) { @@ -14212,9 +14309,15 @@ tcp_rcv_enqueue(tcp, mp, seg_len); flags |= tcp_rcv_drain(tcp->tcp_rq, tcp); } else { - putnext(tcp->tcp_rq, mp); - if (!canputnext(tcp->tcp_rq)) - tcp->tcp_rwnd -= seg_len; + /* Does this need SSL processing first? */ + if ((tcp->tcp_kssl_ctx != NULL) && + (DB_TYPE(mp) == M_DATA)) { + tcp_kssl_input(tcp, mp); + } else { + putnext(tcp->tcp_rq, mp); + if (!canputnext(tcp->tcp_rq)) + tcp->tcp_rwnd -= seg_len; + } } } else { /* @@ -16561,7 +16664,7 @@ * NOTE: the logic of the fast path is duplicated from tcp_wput_data() */ /* ARGSUSED */ -static void +void tcp_output(void *arg, mblk_t *mp, void *arg2) { int len; @@ -17024,6 +17127,26 @@ tcp_wroff_xtra); } + /* + * If this is endpoint is handling SSL, then reserve extra + * offset and space at the end. + * Also have the stream head allocate SSL3_MAX_RECORD_LEN packets, + * overriding the previous setting. The extra cost of signing and + * encrypting multiple MSS-size records (12 of them with Ethernet), + * instead of a single contiguous one by the stream head + * largely outweighs the statistical reduction of ACKs, when + * applicable. The peer will also save on decyption and verification + * costs. + */ + if (tcp->tcp_kssl_ctx != NULL) { + stropt->so_wroff += SSL3_WROFFSET; + + stropt->so_flags |= SO_TAIL; + stropt->so_tail = SSL3_MAX_TAIL_LEN; + + stropt->so_maxblk = SSL3_MAX_RECORD_LEN; + } + /* Send the options up */ putnext(q, stropt_mp); @@ -17335,12 +17458,16 @@ * order as how the 3WHS is completed. */ while (tcp != listener) { - if (!tcp->tcp_eager_prev_q0->tcp_conn_def_q0) + if (!tcp->tcp_eager_prev_q0->tcp_conn_def_q0 && + !tcp->tcp_kssl_pending) break; else tcp = tcp->tcp_eager_prev_q0; } - ASSERT(tcp != listener); + /* None of the pending eagers can be sent up now */ + if (tcp == listener) + goto no_more_eagers; + mp1 = tcp->tcp_conn.tcp_eager_conn_ind; tcp->tcp_conn.tcp_eager_conn_ind = NULL; /* Move from q0 to q */ @@ -17376,6 +17503,7 @@ tcp_send_pending, listener->tcp_connp, SQTAG_TCP_SEND_PENDING); } +no_more_eagers: tcp_eager_unlink(eager); mutex_exit(&listener->tcp_eager_lock); @@ -20577,6 +20705,28 @@ non_urgent_data: switch ((int)tprim->type) { + case T_SSL_PROXY_BIND_REQ: /* an SSL proxy endpoint bind request */ + /* + * save the kssl_ent_t from the next block, and convert this + * back to a normal bind_req. + */ + if (mp->b_cont != NULL) { + ASSERT(MBLKL(mp->b_cont) >= sizeof (kssl_ent_t)); + + if (tcp->tcp_kssl_ent != NULL) { + kssl_release_ent(tcp->tcp_kssl_ent, NULL, + KSSL_NO_PROXY); + tcp->tcp_kssl_ent = NULL; + } + bcopy(mp->b_cont->b_rptr, &tcp->tcp_kssl_ent, + sizeof (kssl_ent_t)); + kssl_hold_ent(tcp->tcp_kssl_ent); + freemsg(mp->b_cont); + mp->b_cont = NULL; + } + tprim->type = T_BIND_REQ; + + /* FALLTHROUGH */ case O_T_BIND_REQ: /* bind request */ case T_BIND_REQ: /* new semantics bind request */ tcp_bind(tcp, mp); diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/common/inet/tcp/tcp_kssl.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/inet/tcp/tcp_kssl.c Sat Nov 12 18:58:05 2005 -0800 @@ -0,0 +1,404 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * For the Kernel SSL proxy + * + * Routines in this file are called on tcp's incoming path, + * tcp_rput_data() mainly, and right before the message is + * to be putnext()'ed upstreams. + */ + +static void tcp_kssl_input_callback(void *, mblk_t *, kssl_cmd_t); +static void tcp_kssl_input_asynch(void *, mblk_t *, void *); + +extern void tcp_output(void *, mblk_t *, void *); +extern void tcp_send_conn_ind(void *, mblk_t *, void *); + +extern squeue_func_t tcp_squeue_wput_proc; + +/* + * tcp_rput_data() calls this routine for all packet destined to a + * connection to the SSL port, when the SSL kernel proxy is configured + * to intercept and process those packets. + * A packet may carry multiple SSL records, so the function + * calls kssl_input() in a loop, until all records are + * handled. + * As long as this conection is in handshake, that is until the first + * time kssl_input() returns a record to be delivered ustreams, + * we maintain the tcp_kssl_inhandshake, and keep an extra reference on + * the tcp/connp across the call to kssl_input(). The reason is, that + * function may return KSSL_CMD_QUEUED after scheduling an asynchronous + * request and cause tcp_kssl_callback() to be called on adifferent CPU, + * which could decrement the conn/tcp reference before we get to increment it. + */ +void +tcp_kssl_input(tcp_t *tcp, mblk_t *mp) +{ + struct conn_s *connp = tcp->tcp_connp; + tcp_t *listener; + mblk_t *ind_mp; + kssl_cmd_t kssl_cmd; + mblk_t *outmp; + struct T_conn_ind *tci; + boolean_t more = B_FALSE; + boolean_t conn_held = B_FALSE; + + /* First time here, allocate the SSL context */ + if (tcp->tcp_kssl_ctx == NULL) { + ASSERT(tcp->tcp_kssl_pending); + + if (kssl_init_context(tcp->tcp_kssl_ent, + tcp->tcp_ipha->ipha_dst, tcp->tcp_mss, + &(tcp->tcp_kssl_ctx)) != KSSL_STS_OK) { + tcp->tcp_kssl_pending = B_FALSE; + kssl_release_ent(tcp->tcp_kssl_ent, NULL, + KSSL_NO_PROXY); + tcp->tcp_kssl_ent = NULL; + goto no_can_do; + } + tcp->tcp_kssl_inhandshake = B_TRUE; + + /* we won't be needing this one after now */ + kssl_release_ent(tcp->tcp_kssl_ent, NULL, KSSL_NO_PROXY); + tcp->tcp_kssl_ent = NULL; + + } + + if (tcp->tcp_kssl_inhandshake) { + CONN_INC_REF(connp); + conn_held = B_TRUE; + } + do { + kssl_cmd = kssl_input(tcp->tcp_kssl_ctx, mp, &outmp, + &more, tcp_kssl_input_callback, (void *)tcp); + + switch (kssl_cmd) { + case KSSL_CMD_SEND: + /* + * We need to increment tcp_squeue_bytes to account + * for the extra bytes internally injected to the + * outgoing flow. tcp_output() will decrement it + * as they are sent out. + */ + ASSERT(!MUTEX_HELD(&connp->conn_lock)); + mutex_enter(&connp->conn_lock); + tcp->tcp_squeue_bytes += msgdsize(outmp); + mutex_exit(&connp->conn_lock); + tcp_output(connp, outmp, NULL); + + /* FALLTHROUGH */ + case KSSL_CMD_NONE: + if (tcp->tcp_kssl_pending) { + mblk_t *ctxmp; + + /* + * SSL handshake successfully started - + * pass up the T_CONN_IND + */ + + mp = NULL; + + listener = tcp->tcp_listener; + tcp->tcp_kssl_pending = B_FALSE; + + ind_mp = tcp->tcp_conn.tcp_eager_conn_ind; + ASSERT(ind_mp != NULL); + + ctxmp = allocb(sizeof (kssl_ctx_t), BPRI_MED); + + /* + * Give this session a chance to fall back to + * userland SSL + */ + if (ctxmp == NULL) + goto no_can_do; + + /* + * attach the kssl_ctx to the conn_ind and + * transform it to a T_SSL_PROXY_CONN_IND. + * Hold it so that it stays valid till it + * reaches the stream head. + */ + kssl_hold_ctx(tcp->tcp_kssl_ctx); + *((kssl_ctx_t *)ctxmp->b_rptr) = + tcp->tcp_kssl_ctx; + ctxmp->b_wptr = ctxmp->b_rptr + + sizeof (kssl_ctx_t); + + ind_mp->b_cont = ctxmp; + + tci = (struct T_conn_ind *)ind_mp->b_rptr; + tci->PRIM_type = T_SSL_PROXY_CONN_IND; + + /* + * The code below is copied from tcp_rput_data() + * delivering the T_CONN_IND on a TCPS_SYN_RCVD, + * and all conn ref cnt comments apply. + */ + tcp->tcp_conn.tcp_eager_conn_ind = NULL; + + CONN_INC_REF(connp); + + CONN_INC_REF(listener->tcp_connp); + if (listener->tcp_connp->conn_sqp == + connp->conn_sqp) { + tcp_send_conn_ind(listener->tcp_connp, + ind_mp, + listener->tcp_connp->conn_sqp); + CONN_DEC_REF(listener->tcp_connp); + } else { + squeue_fill( + listener->tcp_connp->conn_sqp, + ind_mp, tcp_send_conn_ind, + listener->tcp_connp, + SQTAG_TCP_CONN_IND); + } + } + break; + + case KSSL_CMD_QUEUED: + /* + * We hold the conn_t here because an asynchronous + * request have been queued and + * tcp_kssl_input_callback() will be called later. + * It will release the conn_t + */ + CONN_INC_REF(connp); + break; + + case KSSL_CMD_DELIVER_PROXY: + case KSSL_CMD_DELIVER_SSL: + /* + * Keep accumulating if not yet accepted. + */ + if (tcp->tcp_listener != NULL) { + tcp_rcv_enqueue(tcp, outmp, msgdsize(outmp)); + } else { + putnext(tcp->tcp_rq, outmp); + } + /* + * We're at a phase where records are sent upstreams, + * past the handshake + */ + tcp->tcp_kssl_inhandshake = B_FALSE; + break; + + case KSSL_CMD_NOT_SUPPORTED: + /* + * Stop the SSL processing by the proxy, and + * switch to the userland SSL + */ + if (tcp->tcp_kssl_pending) { + + tcp->tcp_kssl_pending = B_FALSE; + +no_can_do: + listener = tcp->tcp_listener; + ind_mp = tcp->tcp_conn.tcp_eager_conn_ind; + ASSERT(ind_mp != NULL); + + if (tcp->tcp_kssl_ctx != NULL) { + kssl_release_ctx(tcp->tcp_kssl_ctx); + tcp->tcp_kssl_ctx = NULL; + } + + /* + * Make this a T_SSL_PROXY_CONN_IND, for the + * stream head to deliver it to the SSL + * fall-back listener + */ + tci = (struct T_conn_ind *)ind_mp->b_rptr; + tci->PRIM_type = T_SSL_PROXY_CONN_IND; + + /* + * The code below is copied from tcp_rput_data() + * delivering the T_CONN_IND on a TCPS_SYN_RCVD, + * and all conn ref cnt comments apply. + */ + tcp->tcp_conn.tcp_eager_conn_ind = NULL; + + CONN_INC_REF(connp); + + CONN_INC_REF(listener->tcp_connp); + if (listener->tcp_connp->conn_sqp == + connp->conn_sqp) { + tcp_send_conn_ind(listener->tcp_connp, + ind_mp, + listener->tcp_connp->conn_sqp); + CONN_DEC_REF(listener->tcp_connp); + } else { + squeue_fill( + listener->tcp_connp->conn_sqp, + ind_mp, tcp_send_conn_ind, + listener->tcp_connp, + SQTAG_TCP_CONN_IND); + } + } + if (mp != NULL) + tcp_rcv_enqueue(tcp, mp, msgdsize(mp)); + break; + } + mp = NULL; + } while (more); + if (conn_held) { + CONN_DEC_REF(connp); + } +} + +/* + * Callback function for the cases kssl_input() had to submit an asynchronous + * job and need to come back when done to carry on the input processing. + * This routine follows the conentions of timeout and interrupt handlers. + * (no blocking, ...) + */ +static void +tcp_kssl_input_callback(void *arg, mblk_t *mp, kssl_cmd_t kssl_cmd) +{ + tcp_t *tcp = (tcp_t *)arg; + conn_t *connp; + mblk_t *sqmp; + + ASSERT(tcp != NULL); + + connp = tcp->tcp_connp; + + ASSERT(connp != NULL); + + switch (kssl_cmd) { + case KSSL_CMD_SEND: + /* I'm coming from an outside perimeter */ + if (mp != NULL) { + /* + * See comment in tcp_kssl_input() call to tcp_output() + */ + ASSERT(!MUTEX_HELD(&connp->conn_lock)); + mutex_enter(&connp->conn_lock); + CONN_INC_REF_LOCKED(connp); + tcp->tcp_squeue_bytes += msgdsize(mp); + mutex_exit(&connp->conn_lock); + } else { + CONN_INC_REF(connp); + } + (*tcp_squeue_wput_proc)(connp->conn_sqp, mp, + tcp_output, connp, SQTAG_TCP_OUTPUT); + + /* FALLTHROUGH */ + case KSSL_CMD_NONE: + break; + + case KSSL_CMD_DELIVER_PROXY: + case KSSL_CMD_DELIVER_SSL: + /* + * Keep accumulating if not yet accepted. + */ + if (tcp->tcp_listener != NULL) { + tcp_rcv_enqueue(tcp, mp, msgdsize(mp)); + } else { + putnext(tcp->tcp_rq, mp); + } + break; + + case KSSL_CMD_NOT_SUPPORTED: + /* Stop the SSL processing */ + kssl_release_ctx(tcp->tcp_kssl_ctx); + tcp->tcp_kssl_ctx = NULL; + } + /* + * Process any input that may have accumulated while we're waiting for + * the call-back. + * We need to re-enter the squeue for this connp, and a new mp is + * necessary. + */ + if ((sqmp = allocb(1, BPRI_MED)) != NULL) { + CONN_INC_REF(connp); + squeue_fill(connp->conn_sqp, sqmp, tcp_kssl_input_asynch, + connp, SQTAG_TCP_KSSL_INPUT); + } +#ifdef DEBUG + else { + cmn_err(CE_WARN, "tcp_kssl_input_callback: alloc failure"); + } +#endif /* DEBUG */ + CONN_DEC_REF(connp); +} + +/* + * Needed by tcp_kssl_input_callback() to continue processing the incoming + * flow on a tcp_t after an asynchronous callback call. + */ +/* ARGSUSED */ +void +tcp_kssl_input_asynch(void *arg, mblk_t *mp, void *arg2) +{ + conn_t *connp = (conn_t *)arg; + tcp_t *tcp = connp->conn_tcp; + + ASSERT(connp != NULL); + freemsg(mp); + + /* + * NULL tcp_kssl_ctx means this connection is getting/was closed + * while we're away + */ + if (tcp->tcp_kssl_ctx != NULL) { + tcp_kssl_input(tcp, NULL); + } +} diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/common/io/stream.c --- a/usr/src/uts/common/io/stream.c Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/uts/common/io/stream.c Sat Nov 12 18:58:05 2005 -0800 @@ -542,6 +542,9 @@ dbp->db_struioflag = 0; dbp->db_struioun.cksum.flags = 0; + /* and the COOKED flag */ + dbp->db_flags &= ~DBLK_COOKED; + kmem_cache_free(dbp->db_cache, dbp); } diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/common/io/strsun.c --- a/usr/src/uts/common/io/strsun.c Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/uts/common/io/strsun.c Sat Nov 12 18:58:05 2005 -0800 @@ -253,6 +253,7 @@ { mblk_t *head = NULL, **tail = &head; size_t offset = stp->sd_wroff; + size_t tail_len = stp->sd_tail; if (iosize == INFPSZ || iosize > uiop->uio_resid) iosize = uiop->uio_resid; @@ -279,7 +280,8 @@ blocksize = MIN(iosize, maxblk); ASSERT(blocksize >= 0); - if ((mp = allocb_cred(offset + blocksize, CRED())) == NULL) { + if ((mp = allocb_cred(offset + blocksize + tail_len, + CRED())) == NULL) { *errorp = ENOMEM; return (head); } diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/common/os/streamio.c --- a/usr/src/uts/common/os/streamio.c Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/uts/common/os/streamio.c Sat Nov 12 18:58:05 2005 -0800 @@ -370,6 +370,7 @@ stp->sd_rerror = 0; stp->sd_werror = 0; stp->sd_wroff = 0; + stp->sd_tail = 0; stp->sd_iocblk = NULL; stp->sd_pushcnt = 0; stp->sd_qn_minpsz = 0; @@ -2269,6 +2270,8 @@ } if (sop->so_flags & SO_WROFF) stp->sd_wroff = sop->so_wroff; + if (sop->so_flags & SO_TAIL) + stp->sd_tail = sop->so_tail; if (sop->so_flags & SO_MINPSZ) q->q_minpsz = sop->so_minpsz; if (sop->so_flags & SO_MAXPSZ) @@ -5884,7 +5887,7 @@ /* * Timed wait for acknowledgment. The wait time is limited by the * timeout value, which must be a positive integer (number of - * milliseconds to wait, or 0 (use default value of STRTIMOUT + * milliseconds) to wait, or 0 (use default value of STRTIMOUT * milliseconds), or -1 (wait forever). This will be awakened * either by an ACK/NAK message arriving, the timer expiring, or * the timer expiring on another ioctl waiting for control of the @@ -7054,6 +7057,19 @@ stp->sd_flag |= STRGETINPROG; mutex_exit(&stp->sd_lock); + if ((stp->sd_rputdatafunc != NULL) && (DB_TYPE(bp) == M_DATA) && + (!(DB_FLAGS(bp) & DBLK_COOKED))) { + + bp = (stp->sd_rputdatafunc)( + stp->sd_vnode, bp, NULL, + NULL, NULL, NULL); + + if (bp == NULL) + goto retry; + + DB_FLAGS(bp) |= DBLK_COOKED; + } + if (STREAM_NEEDSERVICE(stp)) stream_runservice(stp); diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/common/os/strsubr.c --- a/usr/src/uts/common/os/strsubr.c Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/uts/common/os/strsubr.c Sat Nov 12 18:58:05 2005 -0800 @@ -2836,6 +2836,8 @@ mblk_t *mp = NULL; mblk_t *bp; int wroff = (int)stp->sd_wroff; + int tail_len = (int)stp->sd_tail; + int extra = wroff + tail_len; int error = 0; ssize_t maxblk; ssize_t count = *iosize; @@ -2860,10 +2862,10 @@ size = MIN(count, maxblk); - while ((bp = allocb_cred(size + wroff, cr)) == NULL) { + while ((bp = allocb_cred(size + extra, cr)) == NULL) { error = EAGAIN; if ((uiop->uio_fmode & (FNDELAY|FNONBLOCK)) || - (error = strwaitbuf(size + wroff, BPRI_MED)) != 0) { + (error = strwaitbuf(size + extra, BPRI_MED)) != 0) { if (count == *iosize) { freemsg(mp); return (error); @@ -2889,6 +2891,7 @@ dp->db_cksumstuff = 0; dp->db_cksumend = size; *(long long *)dp->db_struioun.data = 0ll; + bp->b_wptr += size; } else { if (stp->sd_copyflag & STRCOPYCACHED) uiop->uio_extflg |= UIO_COPY_CACHED; @@ -2902,9 +2905,22 @@ return (error); } } - } - - bp->b_wptr += size; + bp->b_wptr += size; + + if (stp->sd_wputdatafunc != NULL) { + mblk_t *newbp; + + newbp = (stp->sd_wputdatafunc)(stp->sd_vnode, + bp, NULL, NULL, NULL, NULL); + if (newbp == NULL) { + freeb(bp); + freemsg(mp); + return (ECOMM); + } + bp = newbp; + } + } + count -= size; if (mp == NULL) @@ -3238,6 +3254,7 @@ stp->sd_rprotofunc = strrput_proto; stp->sd_rmiscfunc = strrput_misc; stp->sd_rderrfunc = stp->sd_wrerrfunc = NULL; + stp->sd_rputdatafunc = stp->sd_wputdatafunc = NULL; stp->sd_ciputctrl = NULL; stp->sd_nciputctrl = 0; stp->sd_qhead = NULL; @@ -3246,6 +3263,7 @@ stp->sd_nqueues = 0; stp->sd_svcflags = 0; stp->sd_copyflag = 0; + return (stp); } @@ -7975,6 +7993,19 @@ mutex_exit(&stp->sd_lock); } +void +strsetrwputdatahooks(vnode_t *vp, msgfunc_t rdatafunc, msgfunc_t wdatafunc) +{ + struct stdata *stp = vp->v_stream; + + mutex_enter(&stp->sd_lock); + + stp->sd_rputdatafunc = rdatafunc; + stp->sd_wputdatafunc = wdatafunc; + + mutex_exit(&stp->sd_lock); +} + /* Used within framework when the queue is already locked */ void qenable_locked(queue_t *q) diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/common/sys/socketvar.h --- a/usr/src/uts/common/sys/socketvar.h Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/uts/common/sys/socketvar.h Sat Nov 12 18:58:05 2005 -0800 @@ -50,6 +50,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -324,6 +325,11 @@ int64_t so_nl7c_rcv_rval; void *so_nl7c_uri; time_t so_nl7c_rtime; + + /* For sockets acting as an in-kernel SSL proxy */ + kssl_endpt_type_t so_kssl_type; /* is proxy/is proxied/none */ + kssl_ent_t so_kssl_ent; /* SSL config entry */ + kssl_ctx_t so_kssl_ctx; /* SSL session context */ }; /* flags */ diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/common/sys/stream.h --- a/usr/src/uts/common/sys/stream.h Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/uts/common/sys/stream.h Sat Nov 12 18:58:05 2005 -0800 @@ -376,6 +376,7 @@ * db_flags values (all implementation private!) */ #define DBLK_REFMIN 0x01 /* min refcnt stored in low bit */ +#define DBLK_COOKED 0x02 /* message has been processed once */ /* * db_struioflag values: @@ -577,6 +578,7 @@ ushort_t so_erropt; /* error option */ ssize_t so_maxblk; /* maximum message block size */ ushort_t so_copyopt; /* copy options (see stropts.h) */ + ushort_t so_tail; /* space available at the end */ }; /* flags for stream options set message */ @@ -603,6 +605,7 @@ #define SO_ERROPT 0x040000 /* set error option */ #define SO_COPYOPT 0x080000 /* copy option(s) present */ #define SO_MAXBLK 0x100000 /* set maximum message block size */ +#define SO_TAIL 0x200000 /* set the extra allocated space */ #ifdef _KERNEL /* diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/common/sys/strsubr.h --- a/usr/src/uts/common/sys/strsubr.h Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/uts/common/sys/strsubr.h Sat Nov 12 18:58:05 2005 -0800 @@ -83,6 +83,7 @@ * sd_sidp * sd_pgidp * sd_wroff + * sd_tail * sd_rerror * sd_werror * sd_pushcnt @@ -181,6 +182,7 @@ ushort_t sd_unused; /* UNUSED, retained for binary */ /* compatibility */ ushort_t sd_wroff; /* write offset */ + ushort_t sd_tail; /* reserved space in written mblks */ int sd_rerror; /* error to return on read ops */ int sd_werror; /* error to return on write ops */ int sd_pushcnt; /* number of pushes done on stream */ @@ -213,7 +215,9 @@ uint_t sd_wput_opt; /* options/flags for write/putmsg */ uint_t sd_read_opt; /* options/flags for strread */ msgfunc_t sd_rprotofunc; /* rput M_*PROTO routine */ + msgfunc_t sd_rputdatafunc; /* read M_DATA routine */ msgfunc_t sd_rmiscfunc; /* rput routine (non-data/proto) */ + msgfunc_t sd_wputdatafunc; /* wput M_DATA routine */ errfunc_t sd_rderrfunc; /* read side error callback */ errfunc_t sd_wrerrfunc; /* write side error callback */ /* @@ -1198,6 +1202,7 @@ extern void strflushrq(vnode_t *, int); extern void strsetrputhooks(vnode_t *, uint_t, msgfunc_t, msgfunc_t); extern void strsetwputhooks(vnode_t *, uint_t, clock_t); +extern void strsetrwputdatahooks(vnode_t *, msgfunc_t, msgfunc_t); extern int strwaitmark(vnode_t *); extern void strsignal_nolock(stdata_t *, int, int32_t); diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/common/sys/strsun.h --- a/usr/src/uts/common/sys/strsun.h Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/uts/common/sys/strsun.h Sat Nov 12 18:58:05 2005 -0800 @@ -46,6 +46,7 @@ #define DB_LIM(mp) ((mp)->b_datap->db_lim) #define DB_REF(mp) ((mp)->b_datap->db_ref) #define DB_TYPE(mp) ((mp)->b_datap->db_type) +#define DB_FLAGS(mp) ((mp)->b_datap->db_flags) #define MBLKL(mp) ((mp)->b_wptr - (mp)->b_rptr) #define MBLKSIZE(mp) ((mp)->b_datap->db_lim - (mp)->b_datap->db_base) diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/common/sys/tihdr.h --- a/usr/src/uts/common/sys/tihdr.h Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/uts/common/sys/tihdr.h Sat Nov 12 18:58:05 2005 -0800 @@ -24,8 +24,8 @@ /* - * Copyright (c) 1995,1996 by Sun Microsystems, Inc. - * All rights reserved. + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. */ #ifndef _SYS_TIHDR_H @@ -171,7 +171,8 @@ #ifdef _KERNEL /* * Sun private TPI extensions. They are currently used for transparently - * passing options through the connection-oriented loopback transport. + * passing options through the connection-oriented loopback transport, + * and for setting the kernel SSL proxy. * Values assigned to them may change. * * T_EXTCONN_IND (extended T_CONN_IND) is used to return dst as well as @@ -180,6 +181,12 @@ #define T_OPTDATA_REQ 0x1001 /* data (with options) request */ #define T_OPTDATA_IND 0x1002 /* data (with options) indication */ #define T_EXTCONN_IND 0x1003 /* extended T_CONN_IND to return dst as well */ + +#define T_SSL_PROXY_BIND_REQ 0x1004 /* extended T_BIND_REQ to carry a */ + /* kssl_entry_t to the transport. */ +#define T_SSL_PROXY_CONN_IND 0x1005 /* conn_ind from an SSL proxy */ + /* endpoint, carrying a kssl_ctx_t */ + #endif /* _KERNEL */ /* diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/common/syscall/sendfile.c --- a/usr/src/uts/common/syscall/sendfile.c Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/uts/common/syscall/sendfile.c Sat Nov 12 18:58:05 2005 -0800 @@ -89,6 +89,7 @@ { struct stdata *stp; struct queue *wqp; + mblk_t *newmp; char waitflag; int tempmode; int error = 0; @@ -137,6 +138,15 @@ do { if (canputnext(wqp)) { mutex_exit(&stp->sd_lock); + if (stp->sd_wputdatafunc != NULL) { + newmp = (stp->sd_wputdatafunc)(vp, mp, NULL, + NULL, NULL, NULL); + if (newmp == NULL) { + /* The caller will free mp */ + return (ECOMM); + } + mp = newmp; + } putnext(wqp, mp); return (0); } @@ -494,6 +504,8 @@ size_t iov_len; mblk_t *head, *tmp; size_t size = total_size; + size_t extra; + int tail_len; fflag = fp->f_flag; vp = fp->f_vnode; @@ -502,8 +514,11 @@ ASSERT(maxblk > 0); wroff = (int)vp->v_stream->sd_wroff; + tail_len = (int)vp->v_stream->sd_tail; + extra = wroff + tail_len; + buf_left = MIN(total_size, maxblk); - head = dmp = allocb(buf_left + wroff, BPRI_HI); + head = dmp = allocb(buf_left + extra, BPRI_HI); if (head == NULL) return (ENOMEM); head->b_wptr = head->b_rptr = head->b_rptr + wroff; @@ -552,7 +567,7 @@ tmp = dmp; buf_left = MIN(total_size, maxblk); iov_len = MIN(buf_left, sfv_len); - dmp = allocb(buf_left + wroff, BPRI_HI); + dmp = allocb(buf_left + extra, BPRI_HI); if (dmp == NULL) { freemsg(head); return (ENOMEM); @@ -647,7 +662,7 @@ tmp = dmp; buf_left = MIN(total_size, maxblk); iov_len = MIN(buf_left, sfv_len); - dmp = allocb(buf_left + wroff, BPRI_HI); + dmp = allocb(buf_left + extra, BPRI_HI); if (dmp == NULL) { VOP_RWUNLOCK(readvp, readflg, NULL); @@ -753,10 +768,23 @@ #endif mblk_t *dmp = NULL; char *buf = NULL; + size_t extra; + int maxblk, wroff, tail_len; + struct sonode *so; + stdata_t *stp; fflag = fp->f_flag; vp = fp->f_vnode; + if (vp->v_type == VSOCK) { + so = VTOSO(vp); + stp = vp->v_stream; + wroff = (int)stp->sd_wroff; + tail_len = (int)stp->sd_tail; + maxblk = (int)stp->sd_maxblk; + extra = wroff + tail_len; + } + auio.uio_extflg = UIO_COPY_DEFAULT; for (i = 0; i < copy_cnt; i++) { if (ISSIG(curthread, JUSTLOOKING)) @@ -829,9 +857,8 @@ /* * Optimize for the socket case */ - int wroff = (int)vp->v_stream->sd_wroff; - dmp = allocb(sfv_len + wroff, BPRI_HI); + dmp = allocb(sfv_len + extra, BPRI_HI); if (dmp == NULL) return (ENOMEM); dmp->b_wptr = dmp->b_rptr = dmp->b_rptr + wroff; @@ -926,6 +953,14 @@ releasef(sfv->sfv_fd); return (ENOMEM); } + } else { + /* + * For sockets acting as an SSL proxy, we + * need to adjust the size to the maximum + * SSL record size set in the stream head. + */ + if (so->so_kssl_ctx != NULL) + size = MIN(size, maxblk); } while (sfv_len > 0) { @@ -934,13 +969,15 @@ iov_len = MIN(size, sfv_len); if (vp->v_type == VSOCK) { - dmp = allocb(iov_len, BPRI_HI); + dmp = allocb(iov_len + extra, BPRI_HI); if (dmp == NULL) { VOP_RWUNLOCK(readvp, readflg, NULL); releasef(sfv->sfv_fd); return (ENOMEM); } + dmp->b_wptr = dmp->b_rptr = + dmp->b_rptr + wroff; ptr = (caddr_t)dmp->b_rptr; } else { ptr = buf; @@ -1129,7 +1166,8 @@ } if ((so->so_state & SS_DIRECT) && - (so->so_priv != NULL)) { + (so->so_priv != NULL) && + (so->so_kssl_ctx == NULL)) { maxblk = ((tcp_t *)so->so_priv)->tcp_mss; } else { maxblk = (int)vp->v_stream->sd_maxblk; diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/intel/Makefile.intel --- a/usr/src/uts/intel/Makefile.intel Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/uts/intel/Makefile.intel Sat Nov 12 18:58:05 2005 -0800 @@ -249,6 +249,7 @@ DRV_KMODS += lofi DRV_KMODS += log DRV_KMODS += logindmux +DRV_KMODS += kssl DRV_KMODS += mm DRV_KMODS += mouse8042 DRV_KMODS += mpt diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/intel/ia32/ml/modstubs.s --- a/usr/src/uts/intel/ia32/ml/modstubs.s Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/uts/intel/ia32/ml/modstubs.s Sat Nov 12 18:58:05 2005 -0800 @@ -922,6 +922,7 @@ NO_UNLOAD_STUB(c2audit, audit_setfsat_path, nomod_zero); NO_UNLOAD_STUB(c2audit, audit_cryptoadm, nomod_zero); NO_UNLOAD_STUB(c2audit, audit_update_context, nomod_zero); + NO_UNLOAD_STUB(c2audit, audit_kssl, nomod_zero); END_MODULE(c2audit); #endif @@ -1154,6 +1155,24 @@ END_MODULE(dld); #endif +/* + * Stubs for kssl, the kernel SSL proxy + */ +#ifndef KSSL_MODULE + MODULE(kssl,drv); + NO_UNLOAD_STUB(kssl, kssl_check_proxy, nomod_zero); + NO_UNLOAD_STUB(kssl, kssl_handle_record, nomod_zero); + NO_UNLOAD_STUB(kssl, kssl_input, nomod_zero); + NO_UNLOAD_STUB(kssl, kssl_build_record, nomod_zero); + NO_UNLOAD_STUB(kssl, kssl_hold_ent, nomod_void); + NO_UNLOAD_STUB(kssl, kssl_release_ent, nomod_void); + NO_UNLOAD_STUB(kssl, kssl_find_fallback, nomod_zero); + NO_UNLOAD_STUB(kssl, kssl_init_context, nomod_zero); + NO_UNLOAD_STUB(kssl, kssl_hold_ctx, nomod_void); + NO_UNLOAD_STUB(kssl, kssl_release_ctx, nomod_void); + END_MODULE(kssl); +#endif + / this is just a marker for the area of text that contains stubs ENTRY_NP(stubs_end) diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/intel/kssl/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/intel/kssl/Makefile Sat Nov 12 18:58:05 2005 -0800 @@ -0,0 +1,86 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# This makefile drives the production of the kernel SSL driver +# kernel module. +# +# intel architecture dependent +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = kssl +OBJECTS = $(KSSL_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(KSSL_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) +CONF_SRCDIR = $(UTSBASE)/common/inet/kssl + +# +# Include common rules. +# +include $(UTSBASE)/intel/Makefile.intel + +# +# Define targets +# +ALL_TARGET = $(BINARY) $(SRC_CONFFILE) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOTLINK) $(ROOT_CONFFILE) + +LDFLAGS += -dy -Nmisc/md5 -Nmisc/kcf + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# +# Include common targets. +# +include $(UTSBASE)/intel/Makefile.targ diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/intel/os/minor_perm --- a/usr/src/uts/intel/os/minor_perm Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/uts/intel/os/minor_perm Sat Nov 12 18:58:05 2005 -0800 @@ -116,3 +116,4 @@ zfs:* 0600 root sys zfs:zfs 0666 root sys scsi_vhci:* 0666 root sys +kssl:* 0666 root sys diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/intel/os/name_to_major --- a/usr/src/uts/intel/os/name_to_major Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/uts/intel/os/name_to_major Sat Nov 12 18:58:05 2005 -0800 @@ -118,4 +118,5 @@ zfs 182 npe 183 pcie_pci 184 +kssl 185 did 239 diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/sparc/Makefile.sparc --- a/usr/src/uts/sparc/Makefile.sparc Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/uts/sparc/Makefile.sparc Sat Nov 12 18:58:05 2005 -0800 @@ -223,7 +223,7 @@ DRV_KMODS += fssnap glm icmp icmp6 ip ip6 ipsecah DRV_KMODS += ipsecesp isp iwscn keysock kmdb kstat ksyms llc1 llc2 DRV_KMODS += lofi -DRV_KMODS += log logindmux mm mpt nca pm poll pool +DRV_KMODS += log logindmux kssl mm mpt nca pm poll pool DRV_KMODS += pseudo ptc ptm pts ptsl ramdisk random rsm rts sad se DRV_KMODS += spdsock sppp sppptun sy sysevent sysmsg DRV_KMODS += tcp tcp6 tl tnf ttymux udp udp6 vol wc winlock zcons diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/sparc/kssl/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/sparc/kssl/Makefile Sat Nov 12 18:58:05 2005 -0800 @@ -0,0 +1,91 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# This makefile drives the production of the kernel SSL driver +# kernel module. +# +# sparc architecture dependent +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = kssl +OBJECTS = $(KSSL_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(KSSL_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) +CONF_SRCDIR = $(UTSBASE)/common/inet/kssl + +# +# Include common rules. +# +include $(UTSBASE)/sparc/Makefile.sparc + +# +# Define targets +# +ALL_TARGET = $(BINARY) $(SRC_CONFFILE) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOTLINK) $(ROOT_CONFFILE) + +# +# lint pass one enforcement +# +CFLAGS += $(CCVERBOSE) + +LDFLAGS += -dy -Nmisc/md5 -Nmisc/kcf + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# +# Include common targets. +# +include $(UTSBASE)/sparc/Makefile.targ diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/sparc/ml/modstubs.s --- a/usr/src/uts/sparc/ml/modstubs.s Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/uts/sparc/ml/modstubs.s Sat Nov 12 18:58:05 2005 -0800 @@ -853,6 +853,7 @@ NO_UNLOAD_STUB(c2audit, audit_setfsat_path, nomod_zero); NO_UNLOAD_STUB(c2audit, audit_cryptoadm, nomod_zero); NO_UNLOAD_STUB(c2audit, audit_update_context, nomod_zero); + NO_UNLOAD_STUB(c2audit, audit_kssl, nomod_zero); END_MODULE(c2audit); #endif @@ -1096,6 +1097,24 @@ END_MODULE(dld); #endif +/* + * Stubs for kssl, the kernel SSL proxy + */ +#ifndef KSSL_MODULE + MODULE(kssl,drv); + NO_UNLOAD_STUB(kssl, kssl_check_proxy, nomod_zero); + NO_UNLOAD_STUB(kssl, kssl_handle_record, nomod_zero); + NO_UNLOAD_STUB(kssl, kssl_input, nomod_zero); + NO_UNLOAD_STUB(kssl, kssl_build_record, nomod_zero); + NO_UNLOAD_STUB(kssl, kssl_hold_ent, nomod_void); + NO_UNLOAD_STUB(kssl, kssl_release_ent, nomod_void); + NO_UNLOAD_STUB(kssl, kssl_find_fallback, nomod_zero); + NO_UNLOAD_STUB(kssl, kssl_init_context, nomod_zero); + NO_UNLOAD_STUB(kssl, kssl_hold_ctx, nomod_void); + NO_UNLOAD_STUB(kssl, kssl_release_ctx, nomod_void); + END_MODULE(kssl); +#endif + ! this is just a marker for the area of text that contains stubs .seg ".text" .global stubs_end diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/sparc/os/minor_perm --- a/usr/src/uts/sparc/os/minor_perm Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/uts/sparc/os/minor_perm Sat Nov 12 18:58:05 2005 -0800 @@ -165,3 +165,4 @@ zfs:* 0600 root sys zfs:zfs 0666 root sys scsi_vhci:* 0666 root sys +kssl:* 0666 root sys diff -r 8bc35ca89c2f -r 64b2a371a6bd usr/src/uts/sparc/os/name_to_major --- a/usr/src/uts/sparc/os/name_to_major Sat Nov 12 07:57:48 2005 -0800 +++ b/usr/src/uts/sparc/os/name_to_major Sat Nov 12 18:58:05 2005 -0800 @@ -204,3 +204,4 @@ i8042 254 kb8042 255 mouse8042 256 +kssl 257