xref: /onnv-gate/usr/src/lib/pkcs11/pkcs11_kms/common/kmsKeystoreUtil.c (revision 12720:3db6e0082404)
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