1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 *
21 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
22 */
23
24 #include <stdio.h>
25 #include <malloc.h>
26 #include <memory.h>
27 #include <strings.h>
28 #include <fcntl.h>
29 #include <errno.h>
30 #include <ctype.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <cryptoutil.h>
34 #include <unistd.h>
35 #include <utmpx.h>
36 #include <pthread.h>
37 #include <pwd.h>
38 #include <sha2.h>
39 #include <security/cryptoki.h>
40 #include <aes_impl.h>
41 #include <sys/avl.h>
42
43 #include "kmsSession.h"
44 #include "kmsGlobal.h"
45 #include "kmsObject.h"
46
47 static CK_RV
48 GetPKCS11StatusFromAgentStatus(KMS_AGENT_STATUS status);
49
50 static char keystore_path[BUFSIZ];
51 static boolean_t keystore_path_initialized = B_FALSE;
52 static time_t last_objlist_mtime = 0;
53 pthread_mutex_t objlist_mutex = PTHREAD_MUTEX_INITIALIZER;
54 pthread_mutex_t flock_mutex = PTHREAD_MUTEX_INITIALIZER;
55
56 static struct flock fl = {
57 0,
58 0,
59 0,
60 0,
61 0,
62 0,
63 {0, 0, 0, 0}
64 };
65
66 #define KEYSTORE_PATH "/var/kms"
67 #define ALTERNATE_KEYSTORE_PATH "KMSTOKEN_DIR"
68 #define KMS_PROFILE_FILENAME "profile.cfg"
69 #define KMS_DATAUNIT_DESCRIPTION "Oracle PKCS11/KMS"
70 #define KMS_ATTR_DESC_PFX "PKCS#11v2.20: "
71 #define KMSTOKEN_CONFIG_FILENAME "kmstoken.cfg"
72 #define KMSTOKEN_LABELLIST_FILENAME "objlabels.lst"
73
74 static void
kms_hash_string(char * label,uchar_t * hash)75 kms_hash_string(char *label, uchar_t *hash)
76 {
77 SHA2_CTX ctx;
78
79 SHA2Init(SHA256, &ctx);
80 SHA2Update(&ctx, label, strlen(label));
81 SHA2Final(hash, &ctx);
82 }
83
84 static char *
get_username(char * username,int len)85 get_username(char *username, int len)
86 {
87 struct passwd pwd, *user_info;
88 long buflen;
89 char *pwdbuf = NULL;
90
91 bzero(username, len);
92
93 buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
94 if (buflen == -1)
95 return (username); /* should not happen */
96
97 pwdbuf = calloc(1, buflen);
98 if (pwdbuf == NULL)
99 return (username); /* zero-ed earlier */
100
101 user_info = getpwuid_r(getuid(), &pwd, pwdbuf, buflen);
102
103 if (user_info != NULL)
104 (void) strlcpy(username, user_info->pw_name, len);
105
106 free(pwdbuf);
107 return (username);
108 }
109
110 static char *
kms_get_keystore_path()111 kms_get_keystore_path()
112 {
113 char *env_val;
114 char username[sizeof (((struct utmpx *)0)->ut_user)];
115
116 if (!keystore_path_initialized) {
117 env_val = getenv(ALTERNATE_KEYSTORE_PATH);
118 bzero(keystore_path, sizeof (keystore_path));
119 /*
120 * If it isn't set or is set to the empty string use the
121 * default location. We need to check for the empty string
122 * because some users "unset" environment variables by giving
123 * them no value, this isn't the same thing as removing it
124 * from the environment.
125 */
126 if ((env_val == NULL) || (strcmp(env_val, "") == 0)) {
127 /* alternate path not specified, use /var/kms/$USER */
128 (void) snprintf(keystore_path,
129 sizeof (keystore_path), "%s/%s",
130 KEYSTORE_PATH,
131 get_username(username, sizeof (username)));
132 } else {
133 (void) strlcpy(keystore_path, env_val,
134 sizeof (keystore_path));
135 }
136 keystore_path_initialized = B_TRUE;
137 }
138 return (keystore_path);
139 }
140
141 static char *
get_non_comment_line(char * cfgbuf,size_t cfglen,char * buf,size_t buflen)142 get_non_comment_line(char *cfgbuf, size_t cfglen, char *buf, size_t buflen)
143 {
144 char *s = cfgbuf;
145 char *end = cfgbuf + cfglen;
146 char *f;
147
148 /* Skip over blank lines CR/LF */
149 while (s < end && (*s == '#' || *s == '\n' || *s == '\r')) {
150 /* check for comment sign */
151 if (*s == '#') {
152 /* skip the rest of the line */
153 while ((*s != '\n' || *s == '\r') && s < end)
154 s++;
155 }
156 if ((s < end) && (*s == '\n' || *s == '\r'))
157 s++;
158 }
159
160 if (s < end) {
161 char save, *e;
162 f = s; /* mark the beginning. */
163 /* Find the end of the line and null terminate it. */
164 while (*s != '\n' && *s != '\r' && *s != '#' && s < end) s++;
165 save = *s;
166 *s = 0x00;
167 (void) strncpy(buf, f, buflen);
168 *s = save;
169
170 /* Strip trailing whitespace */
171 f = buf;
172 e = f + strlen(buf) - 1;
173 while (e >= f && isspace(*e)) {
174 *e = 0x00;
175 e--;
176 }
177
178 } else {
179 /* If we reached the end, return NULL */
180 s = NULL;
181 }
182 done:
183 return (s);
184 }
185
186 static int
flock_fd(int fd,int cmd,pthread_mutex_t * mutex)187 flock_fd(int fd, int cmd, pthread_mutex_t *mutex)
188 {
189 int ret = 0;
190
191 (void) pthread_mutex_lock(mutex);
192
193 fl.l_type = cmd;
194
195 while ((ret = fcntl(fd, F_SETLKW, &fl)) == -1) {
196 if (errno != EINTR)
197 break;
198 }
199 (void) pthread_mutex_unlock(mutex);
200 return (ret);
201 }
202
203 /*
204 * Open the keystore description file in the specified mode.
205 * If the keystore doesn't exist, the "do_create_keystore"
206 * argument determines if the keystore should be created
207 */
208 static int
open_and_lock_file(char * filename,int cmd,mode_t mode,pthread_mutex_t * mutex)209 open_and_lock_file(char *filename, int cmd, mode_t mode,
210 pthread_mutex_t *mutex)
211 {
212 int fd;
213
214 fd = open_nointr(filename, mode|O_NONBLOCK);
215 if (fd < 0)
216 return (fd);
217
218 if (flock_fd(fd, cmd, mutex)) {
219 if (fd > 0)
220 (void) close(fd);
221 return (-1);
222 }
223
224 return (fd);
225 }
226
227 static int
kms_slurp_file(char * file,char * buf,size_t buflen)228 kms_slurp_file(char *file, char *buf, size_t buflen)
229 {
230 int n, fd, total = 0;
231
232 fd = open_and_lock_file(file, F_RDLCK, O_RDONLY, &flock_mutex);
233 if (fd == -1)
234 return (-1);
235
236 do {
237 n = readn_nointr(fd, &buf[total], buflen - total);
238 if (n != (buflen - total))
239 break;
240 else
241 total += n;
242 } while (total < buflen);
243
244 if (flock_fd(fd, F_UNLCK, &flock_mutex))
245 total = -1;
246
247 (void) close(fd);
248
249 return (total);
250 }
251
252 /*
253 * The KMS token is considered "initialized" if the file with the token
254 * configuration information is present.
255 */
256 CK_BBOOL
kms_is_initialized()257 kms_is_initialized()
258 {
259 CK_BBOOL rv;
260 char *ksdir;
261 char cfgfile_path[BUFSIZ];
262 struct stat statp;
263
264 ksdir = kms_get_keystore_path();
265 if (ksdir == NULL)
266 return (CKR_FUNCTION_FAILED);
267
268 (void) snprintf(cfgfile_path, sizeof (cfgfile_path),
269 "%s/%s", ksdir, KMSTOKEN_CONFIG_FILENAME);
270
271 if (stat(cfgfile_path, &statp))
272 rv = FALSE;
273 else
274 rv = TRUE;
275
276 return (rv);
277 }
278
279 static CK_RV
kms_read_config_data(char * path,kms_cfg_info_t * cfginfo)280 kms_read_config_data(char *path, kms_cfg_info_t *cfginfo)
281 {
282 CK_RV rv = CKR_OK;
283 char *cfgbuf = NULL;
284 char *ptr;
285 char buf[BUFSIZ];
286 size_t buflen = 0, remain;
287 struct stat statp;
288
289 if (path == NULL || cfginfo == NULL)
290 return (CKR_ARGUMENTS_BAD);
291
292 if (stat(path, &statp) == -1) {
293 return (CKR_FUNCTION_FAILED);
294 }
295
296 cfgbuf = calloc(1, statp.st_size);
297 if (cfgbuf == NULL)
298 return (CKR_HOST_MEMORY);
299
300 buflen = kms_slurp_file(path, cfgbuf, statp.st_size);
301 if (buflen != statp.st_size) {
302 free(cfgbuf);
303 return (CKR_FUNCTION_FAILED);
304 }
305
306 remain = buflen;
307 ptr = cfgbuf;
308 ptr = get_non_comment_line(ptr, remain,
309 cfginfo->name, sizeof (cfginfo->name));
310 if (ptr == NULL) {
311 rv = CKR_FUNCTION_FAILED;
312 goto done;
313 }
314 remain = buflen - (ptr - cfgbuf);
315 ptr = get_non_comment_line(ptr, remain,
316 cfginfo->agentId, sizeof (cfginfo->agentId));
317 if (ptr == 0) {
318 rv = CKR_FUNCTION_FAILED;
319 goto done;
320 }
321 remain = buflen - (ptr - cfgbuf);
322 ptr = get_non_comment_line(ptr, remain,
323 cfginfo->agentAddr, sizeof (cfginfo->agentAddr));
324 if (ptr == 0) {
325 rv = CKR_FUNCTION_FAILED;
326 goto done;
327 }
328 remain = buflen - (ptr - cfgbuf);
329 ptr = get_non_comment_line(ptr, remain, buf, sizeof (buf));
330 if (ptr == 0) {
331 rv = CKR_FUNCTION_FAILED;
332 goto done;
333 }
334 cfginfo->transTimeout = atoi(buf);
335
336 remain = buflen - (ptr - cfgbuf);
337 ptr = get_non_comment_line(ptr, remain, buf, sizeof (buf));
338 if (ptr == 0) {
339 rv = CKR_FUNCTION_FAILED;
340 goto done;
341 }
342 cfginfo->failoverLimit = atoi(buf);
343
344 remain = buflen - (ptr - cfgbuf);
345 ptr = get_non_comment_line(ptr, remain, buf, sizeof (buf));
346 if (ptr == 0) {
347 rv = CKR_FUNCTION_FAILED;
348 goto done;
349 }
350 cfginfo->discoveryFreq = atoi(buf);
351
352 remain = buflen - (ptr - cfgbuf);
353 ptr = get_non_comment_line(ptr, remain, buf, sizeof (buf));
354 if (ptr == 0) {
355 rv = CKR_FUNCTION_FAILED;
356 goto done;
357 }
358 cfginfo->securityMode = atoi(buf);
359 done:
360 if (cfgbuf != NULL)
361 free(cfgbuf);
362 return (rv);
363 }
364
365 CK_BBOOL
kms_is_pin_set()366 kms_is_pin_set()
367 {
368 CK_BBOOL rv = TRUE;
369 kms_cfg_info_t kmscfg;
370 struct stat statp;
371 char *ksdir;
372 char filepath[BUFSIZ];
373
374 ksdir = kms_get_keystore_path();
375 if (ksdir == NULL)
376 return (FALSE);
377
378 (void) snprintf(filepath, sizeof (filepath),
379 "%s/%s", ksdir, KMSTOKEN_CONFIG_FILENAME);
380
381 if ((rv = kms_read_config_data(filepath, &kmscfg)))
382 return (FALSE);
383
384 /*
385 * The PK12 file is only established once the user has enrolled
386 * and is thus considered having a PIN set.
387 */
388 (void) snprintf(filepath, sizeof (filepath),
389 "%s/%s/%s", ksdir, kmscfg.agentId, CLIENT_PK12_FILE);
390
391 if (stat(filepath, &statp))
392 rv = FALSE; /* file doesn't exist. */
393 else
394 rv = TRUE; /* File exists, PIN is set */
395
396 return (rv);
397 }
398
399 void
kms_clear_label_list(avl_tree_t * tree)400 kms_clear_label_list(avl_tree_t *tree)
401 {
402 void *cookie = NULL;
403 objlabel_t *node;
404
405 while ((node = avl_destroy_nodes(tree, &cookie)) != NULL) {
406 free(node->label);
407 free(node);
408 }
409 }
410
411 static void
add_label_node(avl_tree_t * tree,char * label)412 add_label_node(avl_tree_t *tree, char *label)
413 {
414 avl_index_t where;
415 objlabel_t *node;
416 objlabel_t *newnode;
417 int i;
418
419 if (tree == NULL || label == NULL)
420 return;
421
422 /* Remove trailing CR */
423 i = strlen(label) - 1;
424 while (i > 0 && label[i] == '\n')
425 label[i--] = 0x00;
426
427 newnode = calloc(1, sizeof (objlabel_t));
428 newnode->label = (char *)strdup(label);
429 if (newnode->label == NULL) {
430 free(newnode);
431 return;
432 }
433 /* see if this entry already exists */
434 node = avl_find(tree, newnode, &where);
435 if (node == NULL) {
436 avl_insert(tree, newnode, where);
437 } else {
438 /* It's a dup, don't add it */
439 free(newnode->label);
440 free(newnode);
441 }
442 }
443
444 CK_RV
kms_reload_labels(kms_session_t * sp)445 kms_reload_labels(kms_session_t *sp)
446 {
447 CK_RV rv = CKR_OK;
448 char *cfgbuf = NULL, *ptr, buffer[BUFSIZ];
449 size_t buflen, remain;
450 struct stat statp;
451 char *ksdir;
452 char labelfile[BUFSIZ];
453
454 ksdir = kms_get_keystore_path();
455 if (ksdir == NULL)
456 return (CKR_GENERAL_ERROR);
457
458 (void) snprintf(labelfile, sizeof (labelfile),
459 "%s/%s", ksdir, KMSTOKEN_LABELLIST_FILENAME);
460
461 bzero(&statp, sizeof (statp));
462 if (stat(labelfile, &statp) == -1) {
463 if (errno == ENOENT) {
464 FILE *fp;
465 /* Create it */
466 fp = fopen(labelfile, "w");
467 if (fp == NULL)
468 return (CKR_GENERAL_ERROR);
469 (void) fclose(fp);
470 }
471 }
472
473 if (statp.st_size == 0) {
474 return (CKR_OK);
475 }
476
477 cfgbuf = calloc(1, statp.st_size);
478 if (cfgbuf == NULL)
479 return (CKR_HOST_MEMORY);
480
481 buflen = kms_slurp_file(labelfile, cfgbuf, statp.st_size);
482 if (buflen != statp.st_size) {
483 free(cfgbuf);
484 return (CKR_FUNCTION_FAILED);
485 }
486
487 if (statp.st_mtime == last_objlist_mtime) {
488 /* No change */
489 goto end;
490 }
491
492 /* If we got here, we need to refresh the entire list */
493 kms_clear_label_list(&sp->objlabel_tree);
494
495 /*
496 * Read each line and add it as a label node.
497 */
498 remain = buflen;
499 ptr = cfgbuf;
500 while (remain > 0) {
501 ptr = get_non_comment_line(ptr, remain,
502 buffer, sizeof (buffer));
503 if (ptr == NULL) {
504 goto end;
505 }
506 add_label_node(&sp->objlabel_tree, buffer);
507 remain = buflen - (ptr - cfgbuf);
508 }
509 end:
510 if (cfgbuf)
511 free(cfgbuf);
512
513 return (rv);
514 }
515
516 static CK_RV
kms_get_object_label(kms_object_t * obj,char * label,int len)517 kms_get_object_label(kms_object_t *obj, char *label, int len)
518 {
519 CK_RV rv = CKR_OK;
520 CK_ATTRIBUTE stLabel;
521
522 bzero(label, len);
523
524 stLabel.type = CKA_LABEL;
525 stLabel.pValue = label;
526 stLabel.ulValueLen = len;
527
528 /*
529 * The caller MUST provide a CKA_LABEL when deleting.
530 */
531 rv = kms_get_attribute(obj, &stLabel);
532
533 return (rv);
534 }
535
536 /*
537 * Retrieve a data unit associated with the label.
538 */
539 static CK_RV
kms_get_data_unit(kms_session_t * session,char * label,KMSAgent_DataUnit * pDataUnit)540 kms_get_data_unit(kms_session_t *session, char *label,
541 KMSAgent_DataUnit *pDataUnit)
542 {
543 KMS_AGENT_STATUS status;
544 const utf8cstr pDescription = KMS_DATAUNIT_DESCRIPTION;
545 uchar_t externalUniqueId[SHA256_DIGEST_LENGTH];
546
547 /* Find the data unit that holds the key */
548 kms_hash_string(label, externalUniqueId);
549
550 status = KMSAgent_RetrieveDataUnitByExternalUniqueID(
551 &session->kmsProfile,
552 (const unsigned char *)externalUniqueId,
553 sizeof (externalUniqueId),
554 label,
555 pDescription,
556 pDataUnit);
557
558 if (status != KMS_AGENT_STATUS_OK) {
559 return (GetPKCS11StatusFromAgentStatus(status));
560 }
561
562 return (CKR_OK);
563 }
564
565 static CK_RV
kms_decode_description(char * description,kms_object_t * pKey)566 kms_decode_description(char *description, kms_object_t *pKey)
567 {
568 CK_RV rv = CKR_OK;
569 char *ptr;
570 uint32_t keylen;
571 u_longlong_t boolattrs;
572
573 /* If it doesn't start with the expected prefix, return */
574 if (strncmp(description, KMS_ATTR_DESC_PFX,
575 strlen(KMS_ATTR_DESC_PFX)))
576 return (rv);
577
578 ptr = description + strlen(KMS_ATTR_DESC_PFX);
579
580 /*
581 * Decode as follows:
582 * CK_OBJECT_CLASS (2 bytes)
583 * CK_KEY_TYPE (2 bytes)
584 * CKA_VALUE_LEN (4 bytes)
585 * CK_CERTIFICATE_TYPE (2 bytes - not used)
586 * CK_MECHANISM_TYPE (4 bytes)
587 * boolean attributes (3 bytes)
588 * extra attributes (1 byte)
589 * non-boolean attributes
590 */
591 if (sscanf(ptr,
592 "%02lx%02lx%02x00%04lx%06llx00",
593 &pKey->class,
594 &pKey->key_type,
595 &keylen,
596 &pKey->mechanism,
597 &boolattrs) != 5)
598 /* We didn't get the full set of attributes */
599 rv = CKR_ATTRIBUTE_TYPE_INVALID;
600 pKey->bool_attr_mask = boolattrs;
601
602 return (rv);
603 }
604
605 /*
606 * Create a new PKCS#11 object record for the KMSAgent_Key.
607 */
608 static CK_RV
kms_new_key_object(char * label,KMSAgent_DataUnit * dataUnit,KMSAgent_Key * pKey,kms_object_t ** pObj)609 kms_new_key_object(
610 char *label,
611 KMSAgent_DataUnit *dataUnit,
612 KMSAgent_Key *pKey,
613 kms_object_t **pObj)
614 {
615 CK_RV rv = CKR_OK;
616 CK_BBOOL bTrue = B_TRUE;
617 CK_KEY_TYPE keytype = CKK_AES;
618 CK_OBJECT_CLASS class = CKO_SECRET_KEY;
619 CK_ULONG keylen;
620 kms_object_t *newObj;
621
622 CK_ATTRIBUTE template[] = {
623 {CKA_TOKEN, NULL, sizeof (bTrue)},
624 {CKA_LABEL, NULL, 0},
625 {CKA_KEY_TYPE, NULL, sizeof (keytype)},
626 {CKA_CLASS, NULL, sizeof (class)},
627 {CKA_VALUE, NULL, NULL},
628 {CKA_VALUE_LEN, NULL, NULL},
629 {CKA_PRIVATE, NULL, sizeof (bTrue)},
630 };
631
632 keylen = (CK_ULONG)pKey->m_iKeyLength;
633
634 template[0].pValue = &bTrue;
635 template[1].pValue = label;
636 template[1].ulValueLen = strlen(label);
637 template[2].pValue = &keytype;
638 template[3].pValue = &class;
639 template[4].pValue = pKey->m_acKey;
640 template[4].ulValueLen = pKey->m_iKeyLength;
641 template[5].pValue = &keylen;
642 template[5].ulValueLen = sizeof (keylen);
643 template[6].pValue = &bTrue;
644
645 newObj = kms_new_object();
646 if (newObj == NULL)
647 return (CKR_HOST_MEMORY);
648
649 /*
650 * Decode the DataUnit description field to find various
651 * object attributes.
652 */
653 rv = kms_decode_description(dataUnit->m_acDescription, newObj);
654 if (rv) {
655 free(newObj);
656 return (rv);
657 }
658 /*
659 * Set the template keytype and class according to the
660 * data parsed from the description.
661 */
662 if (newObj->key_type)
663 keytype = newObj->key_type;
664 if (newObj->class)
665 class = newObj->class;
666
667 rv = kms_build_object(template, 7, newObj);
668 if (rv) {
669 free(newObj);
670 return (rv);
671 }
672
673 newObj->bool_attr_mask |= TOKEN_BOOL_ON;
674
675 *pObj = newObj;
676 return (rv);
677 }
678
679 static CK_RV
kms_get_data_unit_keys(kms_session_t * sp,KMSAgent_DataUnit * dataUnit,KMSAgent_ArrayOfKeys ** keylist,int * numkeys)680 kms_get_data_unit_keys(kms_session_t *sp, KMSAgent_DataUnit *dataUnit,
681 KMSAgent_ArrayOfKeys **keylist, int *numkeys)
682 {
683 CK_RV rv = CKR_OK;
684 KMSAgent_ArrayOfKeys *kmskeys = NULL;
685 KMS_AGENT_STATUS status;
686 int keysLeft = 0;
687
688 status = KMSAgent_RetrieveDataUnitKeys(
689 &sp->kmsProfile, dataUnit,
690 KMS_MAX_PAGE_SIZE, 0,
691 (int * const)&keysLeft,
692 NULL, /* KeyID */
693 &kmskeys);
694
695 if (status != KMS_AGENT_STATUS_OK) {
696 return (GetPKCS11StatusFromAgentStatus(status));
697 }
698
699 if (keylist != NULL && kmskeys != NULL)
700 *keylist = kmskeys;
701
702 if (numkeys != NULL && kmskeys != NULL)
703 *numkeys = kmskeys->m_iSize;
704
705 if (keylist == NULL && kmskeys != NULL)
706 KMSAgent_FreeArrayOfKeys(kmskeys);
707
708 return (rv);
709 }
710
711
712 /*
713 * Retrieve a key from KMS. We can't use "RetrieveKey" because
714 * we don't know the key id. Instead get all keys associated
715 * with our data unit (there should be only 1.
716 */
717 CK_RV
KMS_RetrieveKeyObj(kms_session_t * sp,char * label,kms_object_t ** pobj)718 KMS_RetrieveKeyObj(kms_session_t *sp, char *label, kms_object_t **pobj)
719 {
720 CK_RV rv = CKR_OK;
721 KMSAgent_DataUnit dataUnit;
722 KMSAgent_ArrayOfKeys *kmsKeys = NULL;
723 KMSAgent_Key *pKey;
724
725 rv = kms_get_data_unit(sp, label, &dataUnit);
726 if (rv != CKR_OK)
727 return (rv);
728
729 rv = kms_get_data_unit_keys(sp, &dataUnit, &kmsKeys, NULL);
730
731 if (rv != CKR_OK || kmsKeys == NULL || kmsKeys->m_iSize == 0)
732 return (CKR_GENERAL_ERROR);
733
734 pKey = &kmsKeys->m_pKeys[0];
735
736 rv = kms_new_key_object(label, &dataUnit, pKey, pobj);
737
738 KMSAgent_FreeArrayOfKeys(kmsKeys);
739 return (rv);
740 }
741
742 CK_RV
KMS_RefreshObjectList(kms_session_t * sp,kms_slot_t * pslot)743 KMS_RefreshObjectList(kms_session_t *sp, kms_slot_t *pslot)
744 {
745 kms_object_t *pObj;
746 char label[BUFSIZ];
747 CK_RV rv;
748 objlabel_t *node;
749
750 rv = kms_reload_labels(sp);
751 if (rv != CKR_OK)
752 return (rv);
753
754 /*
755 * If an object is not in the list, reload it from KMS.
756 */
757 node = avl_first(&sp->objlabel_tree);
758 while (node != NULL) {
759 boolean_t found = FALSE;
760 /* Search object list for matching object */
761 pObj = pslot->sl_tobj_list;
762 while (pObj != NULL && !found) {
763 (void) pthread_mutex_lock(&pObj->object_mutex);
764 if ((rv = kms_get_object_label(pObj, label,
765 sizeof (label))) != CKR_OK) {
766 (void) pthread_mutex_unlock(
767 &pObj->object_mutex);
768 return (rv);
769 }
770 (void) pthread_mutex_unlock(&pObj->object_mutex);
771 found = (strcmp(label, node->label) == 0);
772 pObj = pObj->next;
773 }
774 if (!found) {
775 /*
776 * Fetch KMS key and prepend it to the
777 * token object list for the slot.
778 */
779 rv = KMS_RetrieveKeyObj(sp, node->label, &pObj);
780 if (rv == CKR_OK) {
781 if (pslot->sl_tobj_list == NULL) {
782 pslot->sl_tobj_list = pObj;
783 pObj->prev = NULL;
784 pObj->next = NULL;
785 } else {
786 pObj->next = pslot->sl_tobj_list;
787 pObj->prev = NULL;
788 pslot->sl_tobj_list = pObj;
789 }
790 }
791 }
792 node = AVL_NEXT(&sp->objlabel_tree, node);
793 }
794 return (rv);
795 }
796
797 CK_RV
KMS_Initialize(void)798 KMS_Initialize(void)
799 {
800 char *ksdir;
801 struct stat fn_stat;
802 KMS_AGENT_STATUS kmsrv;
803
804 ksdir = kms_get_keystore_path();
805 if (ksdir == NULL)
806 return (CKR_GENERAL_ERROR);
807
808 /*
809 * If the keystore directory doesn't exist, create it.
810 */
811 if ((stat(ksdir, &fn_stat) != 0) && (errno == ENOENT)) {
812 if (mkdir(ksdir, S_IRUSR|S_IWUSR|S_IXUSR) < 0) {
813 if (errno != EEXIST)
814 return (CKR_GENERAL_ERROR);
815 }
816 }
817
818 if ((kmsrv = KMSAgent_InitializeLibrary(ksdir, FALSE)) !=
819 KMS_AGENT_STATUS_OK) {
820 return (GetPKCS11StatusFromAgentStatus(kmsrv));
821 }
822
823 return (CKR_OK);
824 }
825
826 CK_RV
KMS_Finalize()827 KMS_Finalize()
828 {
829 last_objlist_mtime = 0;
830
831 return (KMSAgent_FinalizeLibrary() == KMS_AGENT_STATUS_OK) ?
832 CKR_OK : CKR_FUNCTION_FAILED;
833 }
834
835 CK_RV
KMS_ChangeLocalPWD(kms_session_t * session,const char * pOldPassword,const char * pNewPassword)836 KMS_ChangeLocalPWD(kms_session_t *session,
837 const char *pOldPassword,
838 const char *pNewPassword)
839 {
840 KMS_AGENT_STATUS status;
841
842 status = KMSAgent_ChangeLocalPWD(
843 &session->kmsProfile,
844 (char * const)pOldPassword,
845 (char * const)pNewPassword);
846
847 return (GetPKCS11StatusFromAgentStatus(status));
848 }
849
850 CK_RV
KMS_GetConfigInfo(kms_cfg_info_t * cfginfo)851 KMS_GetConfigInfo(kms_cfg_info_t *cfginfo)
852 {
853 CK_RV rv = CKR_OK;
854 char cfgfile_path[BUFSIZ];
855 char *ksdir = kms_get_keystore_path();
856
857 if (ksdir == NULL)
858 return (CKR_GENERAL_ERROR);
859
860 (void) snprintf(cfgfile_path, sizeof (cfgfile_path),
861 "%s/%s", ksdir, KMSTOKEN_CONFIG_FILENAME);
862
863 rv = kms_read_config_data(cfgfile_path, cfginfo);
864
865 return (rv);
866 }
867
868 CK_RV
KMS_LoadProfile(KMSClientProfile * profile,kms_cfg_info_t * kmscfg,const char * pPassword,size_t iPasswordLength)869 KMS_LoadProfile(KMSClientProfile *profile,
870 kms_cfg_info_t *kmscfg,
871 const char *pPassword,
872 size_t iPasswordLength)
873 {
874 KMS_AGENT_STATUS status;
875 CK_RV rv;
876 char *sPassword;
877 char cfgfile_path[BUFSIZ];
878 char *ksdir;
879
880 sPassword = calloc(1, iPasswordLength + 1);
881 if (sPassword == NULL)
882 return (CKR_FUNCTION_FAILED);
883
884 (void) memcpy(sPassword, pPassword, iPasswordLength);
885
886 ksdir = kms_get_keystore_path();
887 if (ksdir == NULL)
888 return (CKR_GENERAL_ERROR);
889
890 (void) snprintf(cfgfile_path, sizeof (cfgfile_path),
891 "%s/%s", ksdir, KMSTOKEN_CONFIG_FILENAME);
892
893 if ((rv = kms_read_config_data(cfgfile_path, kmscfg))) {
894 free(sPassword);
895 return (rv);
896 }
897
898 /* First, try to load existing profile */
899 status = KMSAgent_LoadProfile(
900 profile,
901 kmscfg->name,
902 kmscfg->agentId,
903 sPassword,
904 kmscfg->agentAddr,
905 kmscfg->transTimeout,
906 kmscfg->failoverLimit,
907 kmscfg->discoveryFreq,
908 kmscfg->securityMode);
909
910 free(sPassword);
911 return (GetPKCS11StatusFromAgentStatus(status));
912 }
913
914 static CK_RV
GetPKCS11StatusFromAgentStatus(KMS_AGENT_STATUS status)915 GetPKCS11StatusFromAgentStatus(KMS_AGENT_STATUS status)
916 {
917 switch (status) {
918 case KMS_AGENT_STATUS_OK:
919 return (CKR_OK);
920
921 case KMS_AGENT_STATUS_GENERIC_ERROR:
922 return (CKR_GENERAL_ERROR);
923
924 case KMS_AGENT_STATUS_NO_MEMORY:
925 return (CKR_HOST_MEMORY);
926
927 case KMS_AGENT_STATUS_INVALID_PARAMETER:
928 return (CKR_ARGUMENTS_BAD);
929
930 case KMS_AGENT_STATUS_PROFILE_NOT_LOADED:
931 return (CKR_CRYPTOKI_NOT_INITIALIZED);
932
933 case KMS_AGENT_STATUS_KMS_UNAVAILABLE:
934 case KMS_AGENT_STATUS_KMS_NO_READY_KEYS:
935 return (CKR_DEVICE_MEMORY);
936
937 case KMS_AGENT_STATUS_NO_FIPS_KMAS_AVAILABLE:
938 return (CKR_GENERAL_ERROR);
939
940 case KMS_AGENT_STATUS_PROFILE_ALREADY_LOADED:
941 return (CKR_USER_ANOTHER_ALREADY_LOGGED_IN);
942
943 case KMS_AGENT_STATUS_FIPS_KAT_AES_KEYWRAP_ERROR:
944 case KMS_AGENT_STATUS_FIPS_KAT_AES_ECB_ERROR:
945 case KMS_AGENT_STATUS_FIPS_KAT_HMAC_SHA1_ERROR:
946 return (CKR_DEVICE_ERROR);
947
948 case KMS_AGENT_STATUS_ACCESS_DENIED:
949 case KMS_AGENT_LOCAL_AUTH_FAILURE:
950 return (CKR_PIN_INCORRECT);
951
952 case KMS_AGENT_STATUS_SERVER_BUSY:
953 case KMS_AGENT_STATUS_EXTERNAL_UNIQUE_ID_EXISTS:
954 case KMS_AGENT_STATUS_DATA_UNIT_ID_NOT_FOUND_EXTERNAL_ID_EXISTS:
955 case KMS_AGENT_STATUS_KEY_DOES_NOT_EXIST:
956 case KMS_AGENT_STATUS_KEY_DESTROYED:
957 case KMS_AGENT_AES_KEY_UNWRAP_ERROR:
958 case KMS_AGENT_AES_KEY_WRAP_SETUP_ERROR:
959 case KMS_AGENT_STATUS_KEY_CALLOUT_FAILURE:
960 default:
961 return (CKR_GENERAL_ERROR);
962 }
963 }
964
965 void
KMS_UnloadProfile(KMSClientProfile * kmsProfile)966 KMS_UnloadProfile(KMSClientProfile *kmsProfile)
967 {
968 (void) KMSAgent_UnloadProfile(kmsProfile);
969 }
970
971 /*
972 * kms_update_label_file
973 *
974 * KMS doesn't provide an API to allow one to query for available
975 * data units (which map 1-1 to keys). To allow for PKCS11 to
976 * query for a list of available objects, we keep a local list
977 * and update it when an object is added or deleted.
978 */
979 static CK_RV
kms_update_label_file(kms_session_t * sp)980 kms_update_label_file(kms_session_t *sp)
981 {
982 CK_RV rv = CKR_OK;
983 objlabel_t *node;
984 char *ksdir, *tmpfile, labelfile[BUFSIZ];
985 FILE *fp;
986 int fd;
987 struct stat statp;
988
989 ksdir = kms_get_keystore_path();
990 if (ksdir == NULL)
991 return (CKR_GENERAL_ERROR);
992
993 (void) snprintf(labelfile, sizeof (labelfile),
994 "%s/%s", ksdir, KMSTOKEN_LABELLIST_FILENAME);
995
996 tmpfile = tempnam(ksdir, "kmspk11");
997 if (tmpfile == NULL)
998 return (CKR_HOST_MEMORY);
999
1000 fp = fopen(tmpfile, "w");
1001 if (fp == NULL) {
1002 free(tmpfile);
1003 return (CKR_GENERAL_ERROR);
1004 }
1005
1006 /* Lock it even though its a temporary file */
1007 fd = fileno(fp);
1008 if ((rv = flock_fd(fd, F_WRLCK, &objlist_mutex))) {
1009 (void) fclose(fp);
1010 free(tmpfile);
1011 return (rv);
1012 }
1013
1014 node = avl_first(&sp->objlabel_tree);
1015 while (node != NULL) {
1016 if (node->label != NULL)
1017 (void) fprintf(fp, "%s\n", node->label);
1018 node = AVL_NEXT(&sp->objlabel_tree, node);
1019 }
1020
1021 /* Update the last mtime */
1022 if (fstat(fd, &statp) == 0) {
1023 last_objlist_mtime = statp.st_mtime;
1024 }
1025
1026 (void) flock_fd(fd, F_UNLCK, &objlist_mutex);
1027 (void) fclose(fp);
1028
1029 (void) unlink(labelfile);
1030 if (rename(tmpfile, labelfile))
1031 rv = CKR_GENERAL_ERROR;
1032
1033 free(tmpfile);
1034 return (rv);
1035 }
1036
1037 /*
1038 * Destroy a key in the KMS by disassociating an entire data unit.
1039 * The KMSAgent API does not have an interface for destroying an
1040 * individual key.
1041 */
1042 CK_RV
KMS_DestroyKey(kms_session_t * session,kms_object_t * i_oKey)1043 KMS_DestroyKey(kms_session_t *session, kms_object_t *i_oKey)
1044 {
1045 CK_RV rv;
1046 KMSAgent_DataUnit oDataUnit;
1047 KMS_AGENT_STATUS status;
1048 char label[BUFSIZ];
1049 objlabel_t labelnode, *tnode;
1050 avl_index_t where = 0;
1051
1052 /*
1053 * The caller MUST provide a CKA_LABEL when deleting.
1054 */
1055 (void) pthread_mutex_lock(&i_oKey->object_mutex);
1056 if ((rv = kms_get_object_label(i_oKey, label, sizeof (label)))) {
1057 (void) pthread_mutex_unlock(&i_oKey->object_mutex);
1058 return (rv);
1059 }
1060
1061 rv = kms_get_data_unit(session, label, &oDataUnit);
1062 if (rv != CKR_OK)
1063 return (rv);
1064
1065 status = KMSAgent_DisassociateDataUnitKeys(
1066 &session->kmsProfile, &oDataUnit);
1067
1068 /*
1069 * Remove the label from the label list and update
1070 * the file that tracks active keys.
1071 */
1072 bzero(&labelnode, sizeof (labelnode));
1073 labelnode.label = label;
1074
1075 if ((tnode = avl_find(&session->objlabel_tree,
1076 &labelnode, &where)) != NULL)
1077 avl_remove(&session->objlabel_tree, tnode);
1078
1079 /* rewrite the list of labels to disk */
1080 rv = kms_update_label_file(session);
1081 if (rv)
1082 /* Ignore error here */
1083 rv = CKR_OK;
1084
1085 (void) pthread_mutex_unlock(&i_oKey->object_mutex);
1086
1087 return (GetPKCS11StatusFromAgentStatus(status));
1088 }
1089
1090 void
kms_encode_attributes(kms_object_t * pKey,char * attrstr,int len)1091 kms_encode_attributes(kms_object_t *pKey, char *attrstr, int len)
1092 {
1093 char *ptr;
1094
1095 bzero(attrstr, len);
1096
1097 (void) strlcpy(attrstr, KMS_ATTR_DESC_PFX, len);
1098 ptr = attrstr + strlen(attrstr);
1099
1100 /*
1101 * Encode as follows:
1102 * CK_OBJECT_CLASS (2 bytes)
1103 * CK_KEY_TYPE (2 bytes)
1104 * CKA_VALUE_LEN (4 bytes)
1105 * CK_CERTIFICATE_TYPE (2 bytes - not used)
1106 * CK_MECHANISM_TYPE (4 bytes)
1107 * boolean attributes (3 bytes)
1108 * extra attributes (1 byte)
1109 * non-boolean attributes
1110 */
1111 (void) snprintf(ptr, len - strlen(attrstr),
1112 "%02x%02x%02x00%04x%06x00",
1113 pKey->class,
1114 pKey->key_type,
1115 32,
1116 pKey->mechanism,
1117 (pKey->bool_attr_mask & 0x00FFFFFF));
1118 }
1119
1120 CK_RV
KMS_GenerateKey(kms_session_t * session,kms_object_t * i_oKey)1121 KMS_GenerateKey(kms_session_t *session, kms_object_t *i_oKey)
1122 {
1123 CK_RV rv;
1124 CK_ATTRIBUTE stLabel;
1125 KMSAgent_DataUnit oDataUnit;
1126 KMSAgent_Key oKey;
1127 KMS_AGENT_STATUS status;
1128 char label[128];
1129 uchar_t externalUniqueId[SHA256_DIGEST_LENGTH];
1130 char pDescription[KMS_MAX_DESCRIPTION + 1];
1131
1132 (void) pthread_mutex_lock(&i_oKey->object_mutex);
1133
1134 stLabel.type = CKA_LABEL;
1135 stLabel.pValue = label;
1136 stLabel.ulValueLen = sizeof (label);
1137
1138 /*
1139 * The caller MUST provide a CKA_LABEL for storing in the KMS.
1140 */
1141 if ((rv = kms_get_attribute(i_oKey, &stLabel)) != CKR_OK) {
1142 (void) pthread_mutex_unlock(&i_oKey->object_mutex);
1143 return (rv);
1144 }
1145
1146 label[stLabel.ulValueLen] = '\0';
1147
1148 kms_hash_string(label, externalUniqueId);
1149
1150 /* Encode attributes in Description */
1151 kms_encode_attributes(i_oKey, pDescription,
1152 sizeof (pDescription));
1153
1154 status = KMSAgent_CreateDataUnit(
1155 &session->kmsProfile,
1156 (const unsigned char *)externalUniqueId,
1157 sizeof (externalUniqueId),
1158 label, /* externalTag */
1159 pDescription,
1160 &oDataUnit);
1161
1162 /*
1163 * If the DataUnit exists, check to see if it has any keys.
1164 * If it has no keys, then it is OK to continue.
1165 */
1166 if (status == KMS_AGENT_STATUS_EXTERNAL_UNIQUE_ID_EXISTS) {
1167 int numkeys = 0;
1168
1169 rv = kms_get_data_unit(session, label, &oDataUnit);
1170 if (rv != CKR_OK)
1171 return (rv);
1172
1173 rv = kms_get_data_unit_keys(session,
1174 &oDataUnit, NULL, &numkeys);
1175
1176 if (rv != CKR_OK || numkeys > 0)
1177 /*
1178 * This would be better if there were PKCS#11
1179 * error codes for duplicate objects or
1180 * something like that.
1181 */
1182 return (CKR_ARGUMENTS_BAD);
1183
1184 /* If no keys associated with data unit, continue */
1185 status = KMS_AGENT_STATUS_OK;
1186 }
1187
1188 if (status != KMS_AGENT_STATUS_OK) {
1189 (void) pthread_mutex_unlock(&i_oKey->object_mutex);
1190 return (GetPKCS11StatusFromAgentStatus(status));
1191 }
1192
1193 status = KMSAgent_CreateKey(&session->kmsProfile,
1194 &oDataUnit, "", &oKey);
1195
1196 if (status != KMS_AGENT_STATUS_OK) {
1197 /*
1198 * Clean up the old data unit.
1199 */
1200 (void) pthread_mutex_unlock(&i_oKey->object_mutex);
1201 return (GetPKCS11StatusFromAgentStatus(status));
1202 }
1203
1204 /*
1205 * KMS Agent only creates AES-256 keys, so ignore what the user
1206 * requested at this point.
1207 */
1208 OBJ_SEC_VALUE(i_oKey) = malloc(oKey.m_iKeyLength);
1209 if (OBJ_SEC_VALUE(i_oKey) == NULL) {
1210 (void) pthread_mutex_unlock(&i_oKey->object_mutex);
1211 return (CKR_HOST_MEMORY);
1212 }
1213 (void) memcpy(OBJ_SEC_VALUE(i_oKey), oKey.m_acKey,
1214 oKey.m_iKeyLength);
1215 OBJ_SEC_VALUE_LEN(i_oKey) = oKey.m_iKeyLength;
1216
1217 /*
1218 * Add the label to the local list of available objects
1219 */
1220 add_label_node(&session->objlabel_tree, label);
1221
1222 rv = kms_update_label_file(session);
1223
1224 (void) pthread_mutex_unlock(&i_oKey->object_mutex);
1225
1226 return (GetPKCS11StatusFromAgentStatus(status));
1227 }
1228