10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 53089Swyllys * Common Development and Distribution License (the "License"). 63089Swyllys * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 223812Shylee * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 270Sstevel@tonic-gate 280Sstevel@tonic-gate /* 290Sstevel@tonic-gate * This file contains the functions that are shared among 300Sstevel@tonic-gate * the various services this tool will ultimately provide. 3117Sdinak * The functions in this file return PKCS#11 CK_RV errors. 3217Sdinak * Only one session and one login per token is supported 3317Sdinak * at this time. 340Sstevel@tonic-gate */ 350Sstevel@tonic-gate 360Sstevel@tonic-gate #include <stdio.h> 370Sstevel@tonic-gate #include <stdlib.h> 380Sstevel@tonic-gate #include <string.h> 390Sstevel@tonic-gate #include <ctype.h> 403089Swyllys #include <sys/types.h> 413089Swyllys #include <sys/stat.h> 423089Swyllys #include <fcntl.h> 433089Swyllys #include <tzfile.h> 440Sstevel@tonic-gate #include <cryptoutil.h> 450Sstevel@tonic-gate #include <security/cryptoki.h> 463089Swyllys #include <kmfapi.h> 470Sstevel@tonic-gate 483089Swyllys #include "common.h" 4917Sdinak 5017Sdinak /* Local status variables. */ 5117Sdinak static boolean_t initialized = B_FALSE; 5217Sdinak static boolean_t session_opened = B_FALSE; 5317Sdinak static boolean_t logged_in = B_FALSE; 5417Sdinak 55864Sdinak /* Supporting structures and global variables for getopt_av(). */ 56864Sdinak typedef struct av_opts_s { 57864Sdinak int shortnm; /* short name character */ 58864Sdinak char *longnm; /* long name string, NOT terminated */ 59864Sdinak int longnm_len; /* length of long name string */ 60864Sdinak boolean_t has_arg; /* takes optional argument */ 61864Sdinak } av_opts; 62864Sdinak static av_opts *opts_av = NULL; 63864Sdinak static const char *_save_optstr = NULL; 64864Sdinak static int _save_numopts = 0; 65864Sdinak 66864Sdinak int optind_av = 1; 67864Sdinak char *optarg_av = NULL; 68864Sdinak 693089Swyllys static void close_sess(CK_SESSION_HANDLE); 703089Swyllys static void logout_token(CK_SESSION_HANDLE); 713089Swyllys 7217Sdinak /* 7317Sdinak * Perform PKCS#11 setup here. Currently only C_Initialize is required, 7417Sdinak * along with setting/resetting state variables. 7517Sdinak */ 7617Sdinak CK_RV 7717Sdinak init_pk11(void) 7817Sdinak { 7917Sdinak CK_RV rv = CKR_OK; 8017Sdinak 8117Sdinak /* If C_Initialize() already called, nothing to do here. */ 8217Sdinak if (initialized == B_TRUE) 8317Sdinak return (CKR_OK); 8417Sdinak 8517Sdinak /* Reset state variables because C_Initialize() not yet done. */ 8617Sdinak session_opened = B_FALSE; 8717Sdinak logged_in = B_FALSE; 8817Sdinak 8917Sdinak /* Initialize PKCS#11 library. */ 9017Sdinak if ((rv = C_Initialize(NULL_PTR)) != CKR_OK && 9117Sdinak rv != CKR_CRYPTOKI_ALREADY_INITIALIZED) { 9217Sdinak return (rv); 9317Sdinak } 9417Sdinak 9517Sdinak initialized = B_TRUE; 9617Sdinak return (CKR_OK); 9717Sdinak } 9817Sdinak 9917Sdinak /* 10017Sdinak * Finalize PKCS#11 library and reset state variables. Open sessions, 10117Sdinak * if any, are closed, and thereby any logins are logged out also. 10217Sdinak */ 10317Sdinak void 10417Sdinak final_pk11(CK_SESSION_HANDLE sess) 10517Sdinak { 10617Sdinak 10717Sdinak /* If the library wasn't initialized, nothing to do here. */ 10817Sdinak if (!initialized) 10917Sdinak return; 11017Sdinak 11117Sdinak /* Make sure the sesion is closed first. */ 11217Sdinak close_sess(sess); 11317Sdinak 11417Sdinak (void) C_Finalize(NULL); 11517Sdinak initialized = B_FALSE; 11617Sdinak } 1170Sstevel@tonic-gate 1180Sstevel@tonic-gate /* 11917Sdinak * Close PKCS#11 session and reset state variables. Any logins are 12017Sdinak * logged out. 12117Sdinak */ 1223089Swyllys static void 12317Sdinak close_sess(CK_SESSION_HANDLE sess) 12417Sdinak { 12517Sdinak 12617Sdinak if (sess == NULL) { 12717Sdinak return; 12817Sdinak } 12917Sdinak 13017Sdinak /* If session is already closed, nothing to do here. */ 13117Sdinak if (!session_opened) 13217Sdinak return; 1330Sstevel@tonic-gate 13417Sdinak /* Make sure user is logged out of token. */ 13517Sdinak logout_token(sess); 13617Sdinak 13717Sdinak (void) C_CloseSession(sess); 13817Sdinak session_opened = B_FALSE; 13917Sdinak } 14017Sdinak 14117Sdinak /* 14217Sdinak * Log user out of token and reset status variable. 14317Sdinak */ 1443089Swyllys static void 14517Sdinak logout_token(CK_SESSION_HANDLE sess) 14617Sdinak { 14717Sdinak 14817Sdinak if (sess == NULL) { 14917Sdinak return; 15017Sdinak } 15117Sdinak 15217Sdinak /* If already logged out, nothing to do here. */ 15317Sdinak if (!logged_in) 15417Sdinak return; 15517Sdinak 15617Sdinak (void) C_Logout(sess); 15717Sdinak logged_in = B_FALSE; 1580Sstevel@tonic-gate } 1590Sstevel@tonic-gate 1600Sstevel@tonic-gate /* 16117Sdinak * Gets PIN from user. Caller needs to free the returned PIN when done. 16217Sdinak * If two prompts are given, the PIN is confirmed with second prompt. 16317Sdinak * Note that getphassphrase() may return data in static memory area. 16417Sdinak */ 16517Sdinak CK_RV 16617Sdinak get_pin(char *prompt1, char *prompt2, CK_UTF8CHAR_PTR *pin, CK_ULONG *pinlen) 16717Sdinak { 16817Sdinak char *save_phrase, *phrase1, *phrase2; 16917Sdinak 1703089Swyllys 1713089Swyllys #ifdef DEBUG 1723089Swyllys if (getenv("TOKENPIN") != NULL) { 1733089Swyllys *pin = (CK_UTF8CHAR_PTR)strdup(getenv("TOKENPIN")); 1743089Swyllys *pinlen = strlen((char *)(*pin)); 1753089Swyllys return (CKR_OK); 1763089Swyllys } 1773089Swyllys #endif /* DEBUG */ 1780Sstevel@tonic-gate 17917Sdinak /* Prompt user for a PIN. */ 18017Sdinak if (prompt1 == NULL) { 18117Sdinak return (CKR_ARGUMENTS_BAD); 18217Sdinak } 18317Sdinak if ((phrase1 = getpassphrase(prompt1)) == NULL) { 18417Sdinak return (CKR_FUNCTION_FAILED); 18517Sdinak } 18617Sdinak 18717Sdinak /* Duplicate 1st PIN in separate chunk of memory. */ 18817Sdinak if ((save_phrase = strdup(phrase1)) == NULL) 18917Sdinak return (CKR_HOST_MEMORY); 19017Sdinak 19117Sdinak /* If second prompt given, PIN confirmation is requested. */ 19217Sdinak if (prompt2 != NULL) { 19317Sdinak if ((phrase2 = getpassphrase(prompt2)) == NULL) { 19417Sdinak free(save_phrase); 19517Sdinak return (CKR_FUNCTION_FAILED); 19617Sdinak } 19717Sdinak if (strcmp(save_phrase, phrase2) != 0) { 19817Sdinak free(save_phrase); 19917Sdinak return (CKR_PIN_INCORRECT); 20017Sdinak } 2010Sstevel@tonic-gate } 2020Sstevel@tonic-gate 20317Sdinak *pin = (CK_UTF8CHAR_PTR)save_phrase; 20417Sdinak *pinlen = strlen(save_phrase); 20517Sdinak return (CKR_OK); 20617Sdinak } 20717Sdinak 20817Sdinak /* 20917Sdinak * Gets yes/no response from user. If either no prompt is supplied, a 21017Sdinak * default prompt is used. If not message for invalid input is supplied, 21117Sdinak * a default will not be provided. If the user provides no response, 21217Sdinak * the input default B_TRUE == yes, B_FALSE == no is returned. 21317Sdinak * Otherwise, B_TRUE is returned for yes, and B_FALSE for no. 21417Sdinak */ 21517Sdinak boolean_t 21617Sdinak yesno(char *prompt, char *invalid, boolean_t dflt) 21717Sdinak { 21817Sdinak char *response, buf[1024]; 21917Sdinak char *yes = gettext("yes"); 22017Sdinak char *no = gettext("no"); 22117Sdinak 2223089Swyllys 2233089Swyllys #ifdef DEBUG 2243089Swyllys /* If debugging or testing, return TRUE and avoid prompting */ 2253089Swyllys if (getenv("TOKENPIN") != NULL) { 2263089Swyllys return (B_TRUE); 2273089Swyllys } 2283089Swyllys #endif /* DEBUG */ 22917Sdinak 23017Sdinak if (prompt == NULL) 23117Sdinak prompt = gettext("Enter (y)es or (n)o? "); 23217Sdinak 23317Sdinak for (;;) { 23417Sdinak /* Prompt user. */ 23517Sdinak (void) printf("%s", prompt); 23617Sdinak (void) fflush(stdout); 23717Sdinak 23817Sdinak /* Get the response. */ 23917Sdinak if ((response = fgets(buf, sizeof (buf), stdin)) == NULL) 24017Sdinak break; /* go to default response */ 24117Sdinak 24217Sdinak /* Skip any leading white space. */ 24317Sdinak while (isspace(*response)) 24417Sdinak response++; 24517Sdinak if (*response == '\0') 24617Sdinak break; /* go to default response */ 24717Sdinak 24817Sdinak /* Is it valid input? Return appropriately. */ 24917Sdinak if (strncasecmp(response, yes, 1) == 0) 25017Sdinak return (B_TRUE); 25117Sdinak if (strncasecmp(response, no, 1) == 0) 25217Sdinak return (B_FALSE); 25317Sdinak 25417Sdinak /* Indicate invalid input, and try again. */ 25517Sdinak if (invalid != NULL) 256*5051Swyllys (void) printf("%s", invalid); 25717Sdinak } 25817Sdinak return (dflt); 25917Sdinak } 26017Sdinak 26117Sdinak /* 26217Sdinak * Gets the list of slots which have tokens in them. Keeps adjusting 26317Sdinak * the size of the slot list buffer until the call is successful or an 26417Sdinak * irrecoverable error occurs. 26517Sdinak */ 26617Sdinak CK_RV 26717Sdinak get_token_slots(CK_SLOT_ID_PTR *slot_list, CK_ULONG *slot_count) 26817Sdinak { 26917Sdinak CK_ULONG tmp_count = 0; 27017Sdinak CK_SLOT_ID_PTR tmp_list = NULL_PTR, tmp2_list = NULL_PTR; 27117Sdinak int rv = CKR_OK; 27217Sdinak 27317Sdinak if (!initialized) 27417Sdinak if ((rv = init_pk11()) != CKR_OK) 27517Sdinak return (rv); 27617Sdinak 27717Sdinak /* 27817Sdinak * Get the slot count first because we don't know how many 27917Sdinak * slots there are and how many of those slots even have tokens. 28017Sdinak * Don't specify an arbitrary buffer size for the slot list; 28117Sdinak * it may be too small (see section 11.5 of PKCS#11 spec). 28217Sdinak * Also select only those slots that have tokens in them, 28317Sdinak * because this tool has no need to know about empty slots. 28417Sdinak */ 28517Sdinak if ((rv = C_GetSlotList(1, NULL_PTR, &tmp_count)) != CKR_OK) 28617Sdinak return (rv); 28717Sdinak 28817Sdinak if (tmp_count == 0) { 28917Sdinak *slot_list = NULL_PTR; 29017Sdinak *slot_count = 0; 29117Sdinak return (CKR_OK); 29217Sdinak } 29317Sdinak 29417Sdinak /* Allocate initial space for the slot list. */ 29517Sdinak if ((tmp_list = (CK_SLOT_ID_PTR) malloc(tmp_count * 29617Sdinak sizeof (CK_SLOT_ID))) == NULL) 29717Sdinak return (CKR_HOST_MEMORY); 29817Sdinak 29917Sdinak /* Then get the slot list itself. */ 30017Sdinak for (;;) { 30117Sdinak if ((rv = C_GetSlotList(1, tmp_list, &tmp_count)) == CKR_OK) { 30217Sdinak *slot_list = tmp_list; 30317Sdinak *slot_count = tmp_count; 30417Sdinak break; 30517Sdinak } 30617Sdinak 30717Sdinak if (rv != CKR_BUFFER_TOO_SMALL) { 30817Sdinak free(tmp_list); 30917Sdinak break; 31017Sdinak } 31117Sdinak 31217Sdinak /* If the number of slots grew, try again. */ 31317Sdinak if ((tmp2_list = (CK_SLOT_ID_PTR) realloc(tmp_list, 31417Sdinak tmp_count * sizeof (CK_SLOT_ID))) == NULL) { 31517Sdinak free(tmp_list); 31617Sdinak rv = CKR_HOST_MEMORY; 31717Sdinak break; 31817Sdinak } 31917Sdinak tmp_list = tmp2_list; 32017Sdinak } 32117Sdinak 32217Sdinak return (rv); 3230Sstevel@tonic-gate } 3240Sstevel@tonic-gate 3250Sstevel@tonic-gate /* 326864Sdinak * Breaks out the getopt-style option string into a structure that can be 327864Sdinak * traversed later for calls to getopt_av(). Option string is NOT altered, 328864Sdinak * but the struct fields point to locations within option string. 329864Sdinak */ 330864Sdinak static int 331864Sdinak populate_opts(char *optstring) 332864Sdinak { 333864Sdinak int i; 334864Sdinak av_opts *temp; 335864Sdinak char *marker; 336864Sdinak 337864Sdinak if (optstring == NULL || *optstring == '\0') 338864Sdinak return (0); 339864Sdinak 340864Sdinak /* 341864Sdinak * This tries to imitate getopt(3c) Each option must conform to: 342864Sdinak * <short name char> [ ':' ] [ '(' <long name string> ')' ] 343864Sdinak * If long name is missing, the short name is used for long name. 344864Sdinak */ 345864Sdinak for (i = 0; *optstring != '\0'; i++) { 346864Sdinak if ((temp = (av_opts *)((i == 0) ? malloc(sizeof (av_opts)) : 347864Sdinak realloc(opts_av, (i+1) * sizeof (av_opts)))) == NULL) { 3483089Swyllys if (opts_av != NULL) 3493089Swyllys free(opts_av); 350864Sdinak opts_av = NULL; 351864Sdinak return (0); 3523089Swyllys } else { 353864Sdinak opts_av = (av_opts *)temp; 3543089Swyllys } 355864Sdinak 3563089Swyllys (void) memset(&opts_av[i], 0, sizeof (av_opts)); 357864Sdinak marker = optstring; /* may need optstring later */ 358864Sdinak 359864Sdinak opts_av[i].shortnm = *marker++; /* set short name */ 360864Sdinak 361864Sdinak if (*marker == ':') { /* check for opt arg */ 362864Sdinak marker++; 363864Sdinak opts_av[i].has_arg = B_TRUE; 364864Sdinak } 365864Sdinak 366864Sdinak if (*marker == '(') { /* check and set long name */ 367864Sdinak marker++; 368864Sdinak opts_av[i].longnm = marker; 369864Sdinak opts_av[i].longnm_len = strcspn(marker, ")"); 370864Sdinak optstring = marker + opts_av[i].longnm_len + 1; 371864Sdinak } else { 372864Sdinak /* use short name option character */ 373864Sdinak opts_av[i].longnm = optstring; 374864Sdinak opts_av[i].longnm_len = 1; 375864Sdinak optstring = marker; 376864Sdinak } 377864Sdinak } 378864Sdinak 379864Sdinak return (i); 380864Sdinak } 381864Sdinak 382864Sdinak /* 383864Sdinak * getopt_av() is very similar to getopt(3c) in that the takes an option 384864Sdinak * string, compares command line arguments for matches, and returns a single 385864Sdinak * letter option when a match is found. However, getopt_av() differs from 386864Sdinak * getopt(3c) by requiring that only longname options and values be found 387864Sdinak * on the command line and all leading dashes are omitted. In other words, 388864Sdinak * it tries to enforce only longname "option=value" arguments on the command 389864Sdinak * line. Boolean options are not allowed either. 390864Sdinak */ 391864Sdinak int 392864Sdinak getopt_av(int argc, char * const *argv, const char *optstring) 393864Sdinak { 394864Sdinak int i; 395864Sdinak int len; 3963089Swyllys char *cur_option; 397864Sdinak 398864Sdinak if (optind_av >= argc) 399864Sdinak return (EOF); 400864Sdinak 401864Sdinak /* First time or when optstring changes from previous one */ 402864Sdinak if (_save_optstr != optstring) { 403864Sdinak if (opts_av != NULL) 404*5051Swyllys free(opts_av); 405864Sdinak opts_av = NULL; 406864Sdinak _save_optstr = optstring; 407864Sdinak _save_numopts = populate_opts((char *)optstring); 408864Sdinak } 409864Sdinak 410864Sdinak for (i = 0; i < _save_numopts; i++) { 4113089Swyllys cur_option = argv[optind_av]; 4123089Swyllys 4133089Swyllys if (strcmp(cur_option, "--") == 0) { 414864Sdinak optind_av++; 415864Sdinak break; 416864Sdinak } 417864Sdinak 4183089Swyllys if (cur_option[0] == '-' && strlen(cur_option) == 2) { 4193089Swyllys len = 1; 4203089Swyllys cur_option++; /* remove "-" */ 4213089Swyllys } else { 4223089Swyllys len = strcspn(cur_option, "="); 4233089Swyllys } 424864Sdinak 4253089Swyllys if (len == opts_av[i].longnm_len && strncmp(cur_option, 426864Sdinak opts_av[i].longnm, opts_av[i].longnm_len) == 0) { 427864Sdinak /* matched */ 428864Sdinak if (!opts_av[i].has_arg) { 429864Sdinak optind_av++; 430864Sdinak return (opts_av[i].shortnm); 431864Sdinak } 432864Sdinak 433864Sdinak /* needs optarg */ 4343089Swyllys if (cur_option[len] == '=') { 4353089Swyllys optarg_av = &(cur_option[len+1]); 436864Sdinak optind_av++; 437864Sdinak return (opts_av[i].shortnm); 438864Sdinak } 439864Sdinak 440864Sdinak optarg_av = NULL; 441864Sdinak optind_av++; 442864Sdinak return ((int)'?'); 443864Sdinak } 444864Sdinak } 445864Sdinak 446864Sdinak return (EOF); 447864Sdinak } 4483089Swyllys 4493089Swyllys KMF_KEYSTORE_TYPE 4503089Swyllys KS2Int(char *keystore_str) 4513089Swyllys { 4523089Swyllys if (keystore_str == NULL) 4533089Swyllys return (0); 4543089Swyllys if (!strcasecmp(keystore_str, "pkcs11")) 4553089Swyllys return (KMF_KEYSTORE_PK11TOKEN); 4563089Swyllys else if (!strcasecmp(keystore_str, "nss")) 4573089Swyllys return (KMF_KEYSTORE_NSS); 4583089Swyllys else if (!strcasecmp(keystore_str, "file")) 4593089Swyllys return (KMF_KEYSTORE_OPENSSL); 4603089Swyllys else 4613089Swyllys return (0); 4623089Swyllys } 4633089Swyllys 4643089Swyllys 4653089Swyllys int 4663089Swyllys Str2KeyType(char *algm, KMF_KEY_ALG *ktype, KMF_ALGORITHM_INDEX *sigAlg) 4673089Swyllys { 4683089Swyllys if (algm == NULL) { 4693089Swyllys *sigAlg = KMF_ALGID_MD5WithRSA; 4703089Swyllys *ktype = KMF_RSA; 4713089Swyllys } else if (strcasecmp(algm, "DSA") == 0) { 4723089Swyllys *sigAlg = KMF_ALGID_SHA1WithDSA; 4733089Swyllys *ktype = KMF_DSA; 4743089Swyllys } else if (strcasecmp(algm, "RSA") == 0) { 4753089Swyllys *sigAlg = KMF_ALGID_MD5WithRSA; 4763089Swyllys *ktype = KMF_RSA; 4773089Swyllys } else { 4783089Swyllys return (-1); 4793089Swyllys } 4803089Swyllys return (0); 4813089Swyllys } 4823089Swyllys 4833089Swyllys int 4843089Swyllys Str2SymKeyType(char *algm, KMF_KEY_ALG *ktype) 4853089Swyllys { 4863089Swyllys if (algm == NULL) 4873089Swyllys *ktype = KMF_AES; 4883089Swyllys else if (strcasecmp(algm, "aes") == 0) 4893089Swyllys *ktype = KMF_AES; 4903089Swyllys else if (strcasecmp(algm, "arcfour") == 0) 4913089Swyllys *ktype = KMF_RC4; 4923089Swyllys else if (strcasecmp(algm, "des") == 0) 4933089Swyllys *ktype = KMF_DES; 4943089Swyllys else if (strcasecmp(algm, "3des") == 0) 4953089Swyllys *ktype = KMF_DES3; 4963812Shylee else if (strcasecmp(algm, "generic") == 0) 4973812Shylee *ktype = KMF_GENERIC_SECRET; 4983089Swyllys else 4993089Swyllys return (-1); 5003089Swyllys 5013089Swyllys return (0); 5023089Swyllys } 5033089Swyllys 5043089Swyllys int 5053089Swyllys Str2Lifetime(char *ltimestr, uint32_t *ltime) 5063089Swyllys { 5073089Swyllys int num; 5083089Swyllys char timetok[6]; 5093089Swyllys 5103089Swyllys if (ltimestr == NULL || !strlen(ltimestr)) { 5113089Swyllys /* default to 1 year lifetime */ 5123089Swyllys *ltime = SECSPERDAY * DAYSPERNYEAR; 5133089Swyllys return (0); 5143089Swyllys } 5153089Swyllys 5163089Swyllys (void) memset(timetok, 0, sizeof (timetok)); 5173089Swyllys if (sscanf(ltimestr, "%d-%06s", &num, timetok) != 2) 5183089Swyllys return (-1); 5193089Swyllys 5203089Swyllys if (!strcasecmp(timetok, "day") || 5213089Swyllys !strcasecmp(timetok, "days")) { 5223089Swyllys *ltime = num * SECSPERDAY; 5233089Swyllys } else if (!strcasecmp(timetok, "hour") || 524*5051Swyllys !strcasecmp(timetok, "hours")) { 5253089Swyllys *ltime = num * SECSPERHOUR; 5263089Swyllys } else if (!strcasecmp(timetok, "year") || 527*5051Swyllys !strcasecmp(timetok, "years")) { 5283089Swyllys *ltime = num * SECSPERDAY * DAYSPERNYEAR; 5293089Swyllys } else { 5303089Swyllys *ltime = 0; 5313089Swyllys return (-1); 5323089Swyllys } 5333089Swyllys 5343089Swyllys return (0); 5353089Swyllys } 5363089Swyllys 5373089Swyllys int 5383089Swyllys OT2Int(char *objclass) 5393089Swyllys { 5403089Swyllys char *c = NULL; 5413089Swyllys int retval = 0; 5423089Swyllys 5433089Swyllys if (objclass == NULL) 5443089Swyllys return (-1); 5453089Swyllys 5463089Swyllys c = strchr(objclass, ':'); 5473089Swyllys if (c != NULL) { 5483089Swyllys if (!strcasecmp(c, ":private")) 5493089Swyllys retval = PK_PRIVATE_OBJ; 5503089Swyllys else if (!strcasecmp(c, ":public")) 5513089Swyllys retval = PK_PUBLIC_OBJ; 5523089Swyllys else if (!strcasecmp(c, ":both")) 5533089Swyllys retval = PK_PRIVATE_OBJ | PK_PUBLIC_OBJ; 5543089Swyllys else /* unrecognized option */ 5553089Swyllys return (-1); 5563089Swyllys 5573089Swyllys *c = '\0'; 5583089Swyllys } 5593089Swyllys 5603089Swyllys if (!strcasecmp(objclass, "public")) { 5613089Swyllys if (retval) 5623089Swyllys return (-1); 563*5051Swyllys return (retval | PK_PUBLIC_OBJ | PK_CERT_OBJ | PK_PUBKEY_OBJ); 5643089Swyllys } else if (!strcasecmp(objclass, "private")) { 5653089Swyllys if (retval) 5663089Swyllys return (-1); 5673089Swyllys return (retval | PK_PRIKEY_OBJ | PK_PRIVATE_OBJ); 5683089Swyllys } else if (!strcasecmp(objclass, "both")) { 5693089Swyllys if (retval) 5703089Swyllys return (-1); 5713089Swyllys return (PK_KEY_OBJ | PK_PUBLIC_OBJ | PK_PRIVATE_OBJ); 5723089Swyllys } else if (!strcasecmp(objclass, "cert")) { 5733089Swyllys return (retval | PK_CERT_OBJ); 5743089Swyllys } else if (!strcasecmp(objclass, "key")) { 5753089Swyllys if (retval == 0) /* return all keys */ 5763089Swyllys return (retval | PK_KEY_OBJ); 5773089Swyllys else if (retval == (PK_PRIVATE_OBJ | PK_PUBLIC_OBJ)) 5783089Swyllys /* return all keys */ 5793089Swyllys return (retval | PK_KEY_OBJ); 5803089Swyllys else if (retval & PK_PUBLIC_OBJ) 5813089Swyllys /* Only return public keys */ 5823089Swyllys return (retval | PK_PUBKEY_OBJ); 5833089Swyllys else if (retval & PK_PRIVATE_OBJ) 5843089Swyllys /* Only return private keys */ 5853089Swyllys return (retval | PK_PRIKEY_OBJ); 5863089Swyllys } else if (!strcasecmp(objclass, "crl")) { 5873089Swyllys if (retval) 5883089Swyllys return (-1); 5893089Swyllys return (retval | PK_CRL_OBJ); 5903089Swyllys } 5913089Swyllys 5923089Swyllys if (retval == 0) /* No matches found */ 5933089Swyllys retval = -1; 5943089Swyllys return (retval); 5953089Swyllys } 5963089Swyllys 5973089Swyllys KMF_ENCODE_FORMAT 5983089Swyllys Str2Format(char *formstr) 5993089Swyllys { 6003089Swyllys if (formstr == NULL || !strcasecmp(formstr, "der")) 6013089Swyllys return (KMF_FORMAT_ASN1); 6023089Swyllys if (!strcasecmp(formstr, "pem")) 6033089Swyllys return (KMF_FORMAT_PEM); 6043089Swyllys if (!strcasecmp(formstr, "pkcs12")) 6053089Swyllys return (KMF_FORMAT_PKCS12); 606*5051Swyllys if (!strcasecmp(formstr, "raw")) 607*5051Swyllys return (KMF_FORMAT_RAWKEY); 6083089Swyllys 6093089Swyllys return (KMF_FORMAT_UNDEF); 6103089Swyllys } 6113089Swyllys 6123089Swyllys 6133089Swyllys KMF_RETURN 6143089Swyllys select_token(void *kmfhandle, char *token, 6153089Swyllys int readonly) 6163089Swyllys { 617*5051Swyllys KMF_ATTRIBUTE attlist[10]; 618*5051Swyllys int i = 0; 619*5051Swyllys KMF_KEYSTORE_TYPE kstype = KMF_KEYSTORE_PK11TOKEN; 6203089Swyllys KMF_RETURN rv = KMF_OK; 6213089Swyllys 6223089Swyllys if (token == NULL) 6233089Swyllys return (KMF_ERR_BAD_PARAMETER); 6243089Swyllys 625*5051Swyllys kmf_set_attr_at_index(attlist, i, 626*5051Swyllys KMF_KEYSTORE_TYPE_ATTR, &kstype, 627*5051Swyllys sizeof (kstype)); 628*5051Swyllys i++; 6293089Swyllys 630*5051Swyllys if (token) { 631*5051Swyllys kmf_set_attr_at_index(attlist, i, 632*5051Swyllys KMF_TOKEN_LABEL_ATTR, token, 633*5051Swyllys strlen(token)); 634*5051Swyllys i++; 635*5051Swyllys } 636*5051Swyllys 637*5051Swyllys kmf_set_attr_at_index(attlist, i, 638*5051Swyllys KMF_READONLY_ATTR, &readonly, 639*5051Swyllys sizeof (readonly)); 640*5051Swyllys i++; 641*5051Swyllys 642*5051Swyllys rv = kmf_configure_keystore(kmfhandle, i, attlist); 6433089Swyllys if (rv == KMF_ERR_TOKEN_SELECTED) 6443089Swyllys rv = KMF_OK; 6453089Swyllys return (rv); 6463089Swyllys } 6473089Swyllys 6483089Swyllys 6493089Swyllys KMF_RETURN 6503089Swyllys configure_nss(void *kmfhandle, char *dir, char *prefix) 6513089Swyllys { 652*5051Swyllys 653*5051Swyllys KMF_ATTRIBUTE attlist[10]; 654*5051Swyllys int i = 0; 655*5051Swyllys KMF_KEYSTORE_TYPE kstype = KMF_KEYSTORE_NSS; 6563089Swyllys KMF_RETURN rv = KMF_OK; 657*5051Swyllys 658*5051Swyllys kmf_set_attr_at_index(attlist, i, 659*5051Swyllys KMF_KEYSTORE_TYPE_ATTR, &kstype, 660*5051Swyllys sizeof (kstype)); 661*5051Swyllys i++; 6623089Swyllys 663*5051Swyllys if (dir) { 664*5051Swyllys kmf_set_attr_at_index(attlist, i, 665*5051Swyllys KMF_DIRPATH_ATTR, dir, 666*5051Swyllys strlen(dir)); 667*5051Swyllys i++; 668*5051Swyllys } 6693089Swyllys 670*5051Swyllys if (prefix) { 671*5051Swyllys kmf_set_attr_at_index(attlist, i, 672*5051Swyllys KMF_CERTPREFIX_ATTR, prefix, 673*5051Swyllys strlen(prefix)); 674*5051Swyllys i++; 675*5051Swyllys 676*5051Swyllys kmf_set_attr_at_index(attlist, i, 677*5051Swyllys KMF_KEYPREFIX_ATTR, prefix, 678*5051Swyllys strlen(prefix)); 679*5051Swyllys i++; 680*5051Swyllys } 681*5051Swyllys 682*5051Swyllys rv = kmf_configure_keystore(kmfhandle, i, attlist); 6833089Swyllys if (rv == KMF_KEYSTORE_ALREADY_INITIALIZED) 6843089Swyllys rv = KMF_OK; 6853089Swyllys 6863089Swyllys return (rv); 6873089Swyllys } 6883089Swyllys 6893089Swyllys 6903089Swyllys KMF_RETURN 6913089Swyllys get_pk12_password(KMF_CREDENTIAL *cred) 6923089Swyllys { 6933089Swyllys KMF_RETURN rv = KMF_OK; 6943089Swyllys char prompt[1024]; 6953089Swyllys 6963089Swyllys /* 6973089Swyllys * Get the password to use for the PK12 encryption. 6983089Swyllys */ 6993089Swyllys (void) strlcpy(prompt, 700*5051Swyllys gettext("Enter password to use for " 701*5051Swyllys "accessing the PKCS12 file: "), sizeof (prompt)); 7023089Swyllys 7033089Swyllys if (get_pin(prompt, NULL, (uchar_t **)&cred->cred, 704*5051Swyllys (ulong_t *)&cred->credlen) != CKR_OK) { 7053089Swyllys cred->cred = NULL; 7063089Swyllys cred->credlen = 0; 7073089Swyllys } 7083089Swyllys 7093089Swyllys return (rv); 7103089Swyllys } 7113089Swyllys 7123089Swyllys 7133089Swyllys #define COUNTRY_PROMPT "Country Name (2 letter code) [US]:" 7143089Swyllys #define STATE_PROMPT "State or Province Name (full name) [Some-State]:" 7153089Swyllys #define LOCALITY_PROMPT "Locality Name (eg, city) []:" 7163089Swyllys #define ORG_PROMPT "Organization Name (eg, company) []:" 7173089Swyllys #define UNIT_PROMPT "Organizational Unit Name (eg, section) []:" 7183089Swyllys #define NAME_PROMPT "Common Name (eg, YOUR name) []:" 7193089Swyllys #define EMAIL_PROMPT "Email Address []:" 7203089Swyllys 7213089Swyllys #define COUNTRY_DEFAULT "US" 7223089Swyllys #define STATE_DEFAULT "Some-State" 7233089Swyllys #define INVALID_INPUT "Invalid input; please re-enter ..." 7243089Swyllys 7253089Swyllys #define SUBNAMESIZ 1024 7263089Swyllys #define RDN_MIN 1 7273089Swyllys #define RDN_MAX 64 7283089Swyllys #define COUNTRYNAME_MIN 2 7293089Swyllys #define COUNTRYNAME_MAX 2 7303089Swyllys 7313089Swyllys static char * 7323089Swyllys get_input_string(char *prompt, char *default_str, int min_len, int max_len) 7333089Swyllys { 7343089Swyllys char buf[1024]; 7353089Swyllys char *response = NULL; 7363089Swyllys char *ret = NULL; 7373089Swyllys int len; 7383089Swyllys 7393089Swyllys for (;;) { 7403089Swyllys (void) printf("\t%s", prompt); 7413089Swyllys (void) fflush(stdout); 7423089Swyllys 7433089Swyllys response = fgets(buf, sizeof (buf), stdin); 7443089Swyllys if (response == NULL) { 7453089Swyllys if (default_str != NULL) { 7463089Swyllys ret = strdup(default_str); 7473089Swyllys } 7483089Swyllys break; 7493089Swyllys } 7503089Swyllys 7513089Swyllys /* Skip any leading white space. */ 7523089Swyllys while (isspace(*response)) 7533089Swyllys response++; 7543089Swyllys if (*response == '\0') { 7553089Swyllys if (default_str != NULL) { 7563089Swyllys ret = strdup(default_str); 7573089Swyllys } 7583089Swyllys break; 7593089Swyllys } 7603089Swyllys 7613089Swyllys len = strlen(response); 7623089Swyllys response[len-1] = '\0'; /* get rid of "LF" */ 7633089Swyllys len--; 7643089Swyllys if (len >= min_len && len <= max_len) { 7653089Swyllys ret = strdup(response); 7663089Swyllys break; 7673089Swyllys } 7683089Swyllys 7693089Swyllys (void) printf("%s\n", INVALID_INPUT); 7703089Swyllys 7713089Swyllys } 7723089Swyllys 7733089Swyllys return (ret); 7743089Swyllys } 7753089Swyllys 7763089Swyllys int 7773089Swyllys get_subname(char **result) 7783089Swyllys { 7793089Swyllys char *country = NULL; 7803089Swyllys char *state = NULL; 7813089Swyllys char *locality = NULL; 7823089Swyllys char *org = NULL; 7833089Swyllys char *unit = NULL; 7843089Swyllys char *name = NULL; 7853089Swyllys char *email = NULL; 7863089Swyllys char *subname = NULL; 7873089Swyllys 7883089Swyllys (void) printf("Entering following fields for subject (a DN) ...\n"); 7893089Swyllys country = get_input_string(COUNTRY_PROMPT, COUNTRY_DEFAULT, 7903089Swyllys COUNTRYNAME_MIN, COUNTRYNAME_MAX); 7913089Swyllys if (country == NULL) 7923089Swyllys return (-1); 7933089Swyllys 7943089Swyllys state = get_input_string(STATE_PROMPT, STATE_DEFAULT, 7953089Swyllys RDN_MIN, RDN_MAX); 7963089Swyllys if (state == NULL) { 7973089Swyllys goto out; 7983089Swyllys } 7993089Swyllys 8003089Swyllys locality = get_input_string(LOCALITY_PROMPT, NULL, RDN_MIN, RDN_MAX); 8013089Swyllys org = get_input_string(ORG_PROMPT, NULL, RDN_MIN, RDN_MAX); 8023089Swyllys unit = get_input_string(UNIT_PROMPT, NULL, RDN_MIN, RDN_MAX); 8033089Swyllys name = get_input_string(NAME_PROMPT, NULL, RDN_MIN, RDN_MAX); 8043089Swyllys email = get_input_string(EMAIL_PROMPT, NULL, RDN_MIN, RDN_MAX); 8053089Swyllys 8063089Swyllys /* Now create a subject name from the input strings */ 8073089Swyllys if ((subname = malloc(SUBNAMESIZ)) == NULL) 8083089Swyllys goto out; 8093089Swyllys 8103089Swyllys (void) memset(subname, 0, SUBNAMESIZ); 8113089Swyllys (void) strlcpy(subname, "C=", SUBNAMESIZ); 8123089Swyllys (void) strlcat(subname, country, SUBNAMESIZ); 8133089Swyllys (void) strlcat(subname, ", ", SUBNAMESIZ); 8143089Swyllys (void) strlcat(subname, "ST=", SUBNAMESIZ); 8153089Swyllys (void) strlcat(subname, state, SUBNAMESIZ); 8163089Swyllys 8173089Swyllys if (locality) { 8183089Swyllys (void) strlcat(subname, ", ", SUBNAMESIZ); 8193089Swyllys (void) strlcat(subname, "L=", SUBNAMESIZ); 8203089Swyllys (void) strlcat(subname, locality, SUBNAMESIZ); 8213089Swyllys } 8223089Swyllys 8233089Swyllys if (org) { 8243089Swyllys (void) strlcat(subname, ", ", SUBNAMESIZ); 8253089Swyllys (void) strlcat(subname, "O=", SUBNAMESIZ); 8263089Swyllys (void) strlcat(subname, org, SUBNAMESIZ); 8273089Swyllys } 8283089Swyllys 8293089Swyllys if (unit) { 8303089Swyllys (void) strlcat(subname, ", ", SUBNAMESIZ); 8313089Swyllys (void) strlcat(subname, "OU=", SUBNAMESIZ); 8323089Swyllys (void) strlcat(subname, unit, SUBNAMESIZ); 8333089Swyllys } 8343089Swyllys 8353089Swyllys if (name) { 8363089Swyllys (void) strlcat(subname, ", ", SUBNAMESIZ); 8373089Swyllys (void) strlcat(subname, "CN=", SUBNAMESIZ); 8383089Swyllys (void) strlcat(subname, name, SUBNAMESIZ); 8393089Swyllys } 8403089Swyllys 8413089Swyllys if (email) { 8423089Swyllys (void) strlcat(subname, ", ", SUBNAMESIZ); 8433089Swyllys (void) strlcat(subname, "E=", SUBNAMESIZ); 8443089Swyllys (void) strlcat(subname, email, SUBNAMESIZ); 8453089Swyllys } 8463089Swyllys 8473089Swyllys out: 8483089Swyllys if (country) 8493089Swyllys free(country); 8503089Swyllys if (state) 8513089Swyllys free(state); 8523089Swyllys if (locality) 8533089Swyllys free(locality); 8543089Swyllys if (org) 8553089Swyllys free(org); 8563089Swyllys if (unit) 8573089Swyllys free(unit); 8583089Swyllys if (name) 8593089Swyllys free(name); 8603089Swyllys if (email) 8613089Swyllys free(email); 8623089Swyllys 8633089Swyllys if (subname == NULL) 8643089Swyllys return (-1); 8653089Swyllys else { 8663089Swyllys *result = subname; 8673089Swyllys return (0); 8683089Swyllys } 8693089Swyllys } 8703089Swyllys 8713089Swyllys /* 8723089Swyllys * Parse a string of KeyUsage values and convert 8733089Swyllys * them to the correct KU Bits. 8743089Swyllys * The field may be marked "critical" by prepending 8753089Swyllys * "critical:" to the list. 8763089Swyllys * EX: critical:digitialSignature,keyEncipherment 8773089Swyllys */ 8783089Swyllys KMF_RETURN 8793089Swyllys verify_keyusage(char *kustr, uint16_t *kubits, int *critical) 8803089Swyllys { 8813089Swyllys KMF_RETURN ret = KMF_OK; 8823089Swyllys uint16_t kuval; 8833089Swyllys char *k; 8843089Swyllys 8853089Swyllys *kubits = 0; 8863089Swyllys if (kustr == NULL || !strlen(kustr)) 8873089Swyllys return (KMF_ERR_BAD_PARAMETER); 8883089Swyllys 8893089Swyllys /* Check to see if this is critical */ 8903089Swyllys if (!strncasecmp(kustr, "critical:", strlen("critical:"))) { 8913089Swyllys *critical = TRUE; 8923089Swyllys kustr += strlen("critical:"); 8933089Swyllys } else { 8943089Swyllys *critical = FALSE; 8953089Swyllys } 8963089Swyllys 8973089Swyllys k = strtok(kustr, ","); 8983089Swyllys while (k != NULL) { 899*5051Swyllys kuval = kmf_string_to_ku(k); 9003089Swyllys if (kuval == 0) { 9013089Swyllys *kubits = 0; 9023089Swyllys return (KMF_ERR_BAD_PARAMETER); 9033089Swyllys } 9043089Swyllys *kubits |= kuval; 9053089Swyllys k = strtok(NULL, ","); 9063089Swyllys } 9073089Swyllys 9083089Swyllys return (ret); 9093089Swyllys } 9103089Swyllys 9113089Swyllys /* 9123089Swyllys * Verify the alternate subject label is real or invalid. 9133089Swyllys * 9143089Swyllys * The field may be marked "critical" by prepending 9153089Swyllys * "critical:" to the list. 9163089Swyllys * EX: "critical:IP=1.2.3.4" 9173089Swyllys */ 9183089Swyllys KMF_RETURN 9193089Swyllys verify_altname(char *arg, KMF_GENERALNAMECHOICES *type, int *critical) 9203089Swyllys { 9213089Swyllys char *p; 9223089Swyllys KMF_RETURN rv = KMF_OK; 9233089Swyllys 9243089Swyllys /* Check to see if this is critical */ 9253089Swyllys if (!strncasecmp(arg, "critical:", strlen("critical:"))) { 9263089Swyllys *critical = TRUE; 9273089Swyllys arg += strlen("critical:"); 9283089Swyllys } else { 9293089Swyllys *critical = FALSE; 9303089Swyllys } 9313089Swyllys 9323089Swyllys /* Make sure there is an "=" sign */ 9333089Swyllys p = strchr(arg, '='); 9343089Swyllys if (p == NULL) 9353089Swyllys return (KMF_ERR_BAD_PARAMETER); 9363089Swyllys 9373089Swyllys p[0] = '\0'; 9383089Swyllys 9393089Swyllys if (strcmp(arg, "IP") == 0) 9403089Swyllys *type = GENNAME_IPADDRESS; 9413089Swyllys else if (strcmp(arg, "DNS") == 0) 9423089Swyllys *type = GENNAME_DNSNAME; 9433089Swyllys else if (strcmp(arg, "EMAIL") == 0) 9443089Swyllys *type = GENNAME_RFC822NAME; 9453089Swyllys else if (strcmp(arg, "URI") == 0) 9463089Swyllys *type = GENNAME_URI; 9473089Swyllys else if (strcmp(arg, "DN") == 0) 9483089Swyllys *type = GENNAME_DIRECTORYNAME; 9493089Swyllys else if (strcmp(arg, "RID") == 0) 9503089Swyllys *type = GENNAME_REGISTEREDID; 9513089Swyllys else 9523089Swyllys rv = KMF_ERR_BAD_PARAMETER; 9533089Swyllys 9543089Swyllys p[0] = '='; 9553089Swyllys 9563089Swyllys return (rv); 9573089Swyllys } 9583089Swyllys 9593089Swyllys int 9603089Swyllys get_token_password(KMF_KEYSTORE_TYPE kstype, 9613089Swyllys char *token_spec, KMF_CREDENTIAL *cred) 9623089Swyllys { 9633089Swyllys char prompt[1024]; 9643089Swyllys char *p = NULL; 9653089Swyllys 9663089Swyllys if (kstype == KMF_KEYSTORE_PK11TOKEN) { 9673089Swyllys p = strchr(token_spec, ':'); 9683089Swyllys if (p != NULL) 9693089Swyllys *p = 0; 9703089Swyllys } 9713089Swyllys /* 9723089Swyllys * Login to the token first. 9733089Swyllys */ 9743089Swyllys (void) snprintf(prompt, sizeof (prompt), 975*5051Swyllys gettext(DEFAULT_TOKEN_PROMPT), token_spec); 9763089Swyllys 9773089Swyllys if (get_pin(prompt, NULL, (uchar_t **)&cred->cred, 978*5051Swyllys (ulong_t *)&cred->credlen) != CKR_OK) { 9793089Swyllys cred->cred = NULL; 9803089Swyllys cred->credlen = 0; 9813089Swyllys } 9823089Swyllys 9833089Swyllys if (kstype == KMF_KEYSTORE_PK11TOKEN && p != NULL) 9843089Swyllys *p = ':'; 9853089Swyllys return (KMF_OK); 9863089Swyllys } 9873089Swyllys 9883089Swyllys KMF_RETURN 9893089Swyllys verify_file(char *filename) 9903089Swyllys { 9913089Swyllys KMF_RETURN ret = KMF_OK; 9923089Swyllys int fd; 9933089Swyllys 9943089Swyllys /* 9953089Swyllys * Attempt to open with the EXCL flag so that if 9963089Swyllys * it already exists, the open will fail. It will 9973089Swyllys * also fail if the file cannot be created due to 9983089Swyllys * permissions on the parent directory, or if the 9993089Swyllys * parent directory itself does not exist. 10003089Swyllys */ 10013089Swyllys fd = open(filename, O_CREAT | O_EXCL, 0600); 10023089Swyllys if (fd == -1) 10033089Swyllys return (KMF_ERR_OPEN_FILE); 10043089Swyllys 10053089Swyllys /* If we were able to create it, delete it. */ 10063089Swyllys (void) close(fd); 10073089Swyllys (void) unlink(filename); 10083089Swyllys 10093089Swyllys return (ret); 10103089Swyllys } 10113089Swyllys 10123089Swyllys void 10133089Swyllys display_error(void *handle, KMF_RETURN errcode, char *prefix) 10143089Swyllys { 10153089Swyllys KMF_RETURN rv1, rv2; 10163089Swyllys char *plugin_errmsg = NULL; 10173089Swyllys char *kmf_errmsg = NULL; 10183089Swyllys 1019*5051Swyllys rv1 = kmf_get_plugin_error_str(handle, &plugin_errmsg); 1020*5051Swyllys rv2 = kmf_get_kmf_error_str(errcode, &kmf_errmsg); 10213089Swyllys 10223089Swyllys cryptoerror(LOG_STDERR, "%s:", prefix); 10233089Swyllys if (rv1 == KMF_OK && plugin_errmsg) { 1024*5051Swyllys cryptoerror(LOG_STDERR, gettext("keystore error: %s"), 1025*5051Swyllys plugin_errmsg); 1026*5051Swyllys kmf_free_str(plugin_errmsg); 10273089Swyllys } 10283089Swyllys 10293089Swyllys if (rv2 == KMF_OK && kmf_errmsg) { 1030*5051Swyllys cryptoerror(LOG_STDERR, gettext("libkmf error: %s"), 1031*5051Swyllys kmf_errmsg); 1032*5051Swyllys kmf_free_str(kmf_errmsg); 10333089Swyllys } 10343089Swyllys 10353089Swyllys if (rv1 != KMF_OK && rv2 != KMF_OK) 10363089Swyllys cryptoerror(LOG_STDERR, gettext("<unknown error>\n")); 10373089Swyllys 10383089Swyllys } 1039