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 2006 Sun Microsystems, Inc.  All rights reserved.
22  * Use is subject to license terms.
23  */
24 
25 #pragma ident	"%Z%%M%	%I%	%E% SMI"
26 
27 #include <stdio.h>
28 #include <strings.h>
29 #include <ctype.h>
30 #include <libgen.h>
31 #include <libintl.h>
32 #include <errno.h>
33 #include <kmfapiP.h>
34 #include <cryptoutil.h>
35 #include "util.h"
36 
37 int
38 kc_create(int argc, char *argv[])
39 {
40 	KMF_RETURN	ret;
41 	int 		rv = KC_OK;
42 	int		opt;
43 	extern int	optind_av;
44 	extern char	*optarg_av;
45 	char		*filename = NULL;
46 	int		ocsp_set_attr = 0;
47 	boolean_t	crl_set_attr = 0;
48 	KMF_POLICY_RECORD plc;
49 
50 	(void) memset(&plc, 0, sizeof (KMF_POLICY_RECORD));
51 
52 	while ((opt = getopt_av(argc, argv,
53 		"i:(dbfile)"
54 		"p:(policy)"
55 		"d:(ignore-date)"
56 		"e:(ignore-unknown-eku)"
57 		"a:(ignore-trust-anchor)"
58 		"v:(validity-adjusttime)"
59 		"t:(ta-name)"
60 		"s:(ta-serial)"
61 		"o:(ocsp-responder)"
62 		"P:(ocsp-proxy)"
63 		"r:(ocsp-use-cert-responder)"
64 		"T:(ocsp-response-lifetime)"
65 		"R:(ocsp-ignore-response-sign)"
66 		"n:(ocsp-responder-cert-name)"
67 		"A:(ocsp-responder-cert-serial)"
68 		"c:(crl-basefilename)"
69 		"I:(crl-directory)"
70 		"g:(crl-get-crl-uri)"
71 		"X:(crl-proxy)"
72 		"S:(crl-ignore-crl-sign)"
73 		"D:(crl-ignore-crl-date)"
74 		"u:(keyusage)"
75 		"E:(ekunames)"
76 		"O:(ekuoids)")) != EOF) {
77 		switch (opt) {
78 			case 'i':
79 				filename = get_string(optarg_av, &rv);
80 				if (filename == NULL) {
81 					(void) fprintf(stderr,
82 					    gettext("Error dbfile input.\n"));
83 				}
84 				break;
85 			case 'p':
86 				plc.name = get_string(optarg_av, &rv);
87 				if (plc.name == NULL) {
88 					(void) fprintf(stderr,
89 					    gettext("Error policy name.\n"));
90 				}
91 				break;
92 			case 'd':
93 				plc.ignore_date = get_boolean(optarg_av);
94 				if (plc.ignore_date == -1) {
95 					(void) fprintf(stderr,
96 					    gettext("Error boolean input.\n"));
97 					rv = KC_ERR_USAGE;
98 				}
99 				break;
100 			case 'e':
101 				plc.ignore_unknown_ekus =
102 				    get_boolean(optarg_av);
103 				if (plc.ignore_unknown_ekus == -1) {
104 					(void) fprintf(stderr,
105 					    gettext("Error boolean input.\n"));
106 					rv = KC_ERR_USAGE;
107 				}
108 				break;
109 			case 'a':
110 				plc.ignore_trust_anchor =
111 				    get_boolean(optarg_av);
112 				if (plc.ignore_trust_anchor == -1) {
113 					(void) fprintf(stderr,
114 					    gettext("Error boolean input.\n"));
115 					rv = KC_ERR_USAGE;
116 				}
117 				break;
118 			case 'v':
119 				plc.validity_adjusttime =
120 				    get_string(optarg_av, &rv);
121 				if (plc.validity_adjusttime == NULL) {
122 					(void) fprintf(stderr,
123 					    gettext("Error time input.\n"));
124 				} else {
125 					uint32_t adj;
126 					/* for syntax checking */
127 					if (str2lifetime(
128 					    plc.validity_adjusttime,
129 					    &adj) < 0) {
130 						(void) fprintf(stderr,
131 						    gettext("Error time "
132 						    "input.\n"));
133 						rv = KC_ERR_USAGE;
134 					}
135 				}
136 				break;
137 			case 't':
138 				plc.ta_name = get_string(optarg_av, &rv);
139 				if (plc.ta_name == NULL) {
140 					(void) fprintf(stderr,
141 					    gettext("Error name input.\n"));
142 				} else {
143 					KMF_X509_NAME taDN;
144 					/* for syntax checking */
145 					if (KMF_DNParser(plc.ta_name,
146 					    &taDN) != KMF_OK) {
147 						(void) fprintf(stderr,
148 						    gettext("Error name "
149 						    "input.\n"));
150 						rv = KC_ERR_USAGE;
151 					} else {
152 						KMF_FreeDN(&taDN);
153 					}
154 				}
155 				break;
156 			case 's':
157 				plc.ta_serial = get_string(optarg_av, &rv);
158 				if (plc.ta_serial == NULL) {
159 					(void) fprintf(stderr,
160 					    gettext("Error serial input.\n"));
161 				} else {
162 					uchar_t *bytes = NULL;
163 					size_t bytelen;
164 
165 					ret = KMF_HexString2Bytes(
166 					    (uchar_t *)plc.ta_serial,
167 					    &bytes, &bytelen);
168 					if (ret != KMF_OK || bytes == NULL) {
169 						(void) fprintf(stderr,
170 						    gettext("serial number "
171 						    "must be specified as a "
172 						    "hex number "
173 						    "(ex: 0x0102030405"
174 						    "ffeeddee)\n"));
175 						rv = KC_ERR_USAGE;
176 					}
177 					if (bytes != NULL)
178 						free(bytes);
179 				}
180 				break;
181 			case 'o':
182 				plc.VAL_OCSP_RESPONDER_URI =
183 				    get_string(optarg_av, &rv);
184 				if (plc.VAL_OCSP_RESPONDER_URI == NULL) {
185 					(void) fprintf(stderr, gettext(
186 					    "Error responder input.\n"));
187 				} else {
188 					ocsp_set_attr++;
189 				}
190 				break;
191 			case 'P':
192 				plc.VAL_OCSP_PROXY =
193 				    get_string(optarg_av, &rv);
194 				if (plc.VAL_OCSP_PROXY == NULL) {
195 					(void) fprintf(stderr,
196 					    gettext("Error proxy input.\n"));
197 				} else {
198 					ocsp_set_attr++;
199 				}
200 				break;
201 			case 'r':
202 				plc.VAL_OCSP_URI_FROM_CERT =
203 				    get_boolean(optarg_av);
204 				if (plc.VAL_OCSP_URI_FROM_CERT == -1) {
205 					(void) fprintf(stderr,
206 					    gettext("Error boolean input.\n"));
207 					rv = KC_ERR_USAGE;
208 				} else {
209 					ocsp_set_attr++;
210 				}
211 				break;
212 			case 'T':
213 				plc.VAL_OCSP_RESP_LIFETIME =
214 				    get_string(optarg_av, &rv);
215 				if (plc.VAL_OCSP_RESP_LIFETIME == NULL) {
216 					(void) fprintf(stderr,
217 					    gettext("Error time input.\n"));
218 				} else {
219 					uint32_t adj;
220 					/* for syntax checking */
221 					if (str2lifetime(
222 					    plc.VAL_OCSP_RESP_LIFETIME,
223 					    &adj) < 0) {
224 						(void) fprintf(stderr,
225 						    gettext("Error time "
226 						    "input.\n"));
227 						rv = KC_ERR_USAGE;
228 					} else {
229 						ocsp_set_attr++;
230 					}
231 				}
232 				break;
233 			case 'R':
234 				plc.VAL_OCSP_IGNORE_RESP_SIGN =
235 				    get_boolean(optarg_av);
236 				if (plc.VAL_OCSP_IGNORE_RESP_SIGN == -1) {
237 					(void) fprintf(stderr,
238 					    gettext("Error boolean input.\n"));
239 					rv = KC_ERR_USAGE;
240 				} else {
241 					ocsp_set_attr++;
242 				}
243 				break;
244 			case 'n':
245 				plc.VAL_OCSP_RESP_CERT_NAME =
246 				    get_string(optarg_av, &rv);
247 				if (plc.VAL_OCSP_RESP_CERT_NAME == NULL) {
248 					(void) fprintf(stderr,
249 					    gettext("Error name input.\n"));
250 				} else {
251 					KMF_X509_NAME respDN;
252 					/* for syntax checking */
253 					if (KMF_DNParser(
254 					    plc.VAL_OCSP_RESP_CERT_NAME,
255 					    &respDN) != KMF_OK) {
256 						(void) fprintf(stderr,
257 						    gettext("Error name "
258 						    "input.\n"));
259 						rv = KC_ERR_USAGE;
260 					} else {
261 						KMF_FreeDN(&respDN);
262 						ocsp_set_attr++;
263 					}
264 				}
265 				break;
266 			case 'A':
267 				plc.VAL_OCSP_RESP_CERT_SERIAL =
268 				    get_string(optarg_av, &rv);
269 				if (plc.VAL_OCSP_RESP_CERT_SERIAL == NULL) {
270 					(void) fprintf(stderr,
271 					    gettext("Error serial input.\n"));
272 				} else {
273 					uchar_t *bytes = NULL;
274 					size_t bytelen;
275 
276 					ret = KMF_HexString2Bytes((uchar_t *)
277 					    plc.VAL_OCSP_RESP_CERT_SERIAL,
278 					    &bytes, &bytelen);
279 					if (ret != KMF_OK || bytes == NULL) {
280 						(void) fprintf(stderr,
281 						    gettext("serial number "
282 						    "must be specified as a "
283 						    "hex number "
284 						    "(ex: 0x0102030405"
285 						    "ffeeddee)\n"));
286 						rv = KC_ERR_USAGE;
287 						break;
288 					}
289 					if (bytes != NULL)
290 						free(bytes);
291 					ocsp_set_attr++;
292 				}
293 				break;
294 			case 'c':
295 				plc.VAL_CRL_BASEFILENAME =
296 				    get_string(optarg_av, &rv);
297 				if (plc.VAL_CRL_BASEFILENAME == NULL) {
298 					(void) fprintf(stderr,
299 					    gettext("Error boolean input.\n"));
300 				} else {
301 					crl_set_attr++;
302 				}
303 				break;
304 			case 'I':
305 				plc.VAL_CRL_DIRECTORY =
306 				    get_string(optarg_av, &rv);
307 				if (plc.VAL_CRL_DIRECTORY == NULL) {
308 					(void) fprintf(stderr,
309 					    gettext("Error boolean input.\n"));
310 				} else {
311 					crl_set_attr++;
312 				}
313 				break;
314 			case 'g':
315 				plc.VAL_CRL_GET_URI = get_boolean(optarg_av);
316 				if (plc.VAL_CRL_GET_URI == -1) {
317 					(void) fprintf(stderr,
318 					    gettext("Error boolean input.\n"));
319 					rv = KC_ERR_USAGE;
320 				} else {
321 					crl_set_attr++;
322 				}
323 				break;
324 			case 'X':
325 				plc.VAL_CRL_PROXY = get_string(optarg_av, &rv);
326 				if (plc.VAL_CRL_PROXY == NULL) {
327 					(void) fprintf(stderr,
328 					    gettext("Error proxy input.\n"));
329 				} else {
330 					crl_set_attr++;
331 				}
332 				break;
333 			case 'S':
334 				plc.VAL_CRL_IGNORE_SIGN =
335 				    get_boolean(optarg_av);
336 				if (plc.VAL_CRL_IGNORE_SIGN == -1) {
337 					(void) fprintf(stderr,
338 					    gettext("Error boolean input.\n"));
339 					rv = KC_ERR_USAGE;
340 				} else {
341 					crl_set_attr++;
342 				}
343 				break;
344 			case 'D':
345 				plc.VAL_CRL_IGNORE_DATE =
346 					get_boolean(optarg_av);
347 				if (plc.VAL_CRL_IGNORE_DATE == -1) {
348 					(void) fprintf(stderr,
349 					    gettext("Error boolean input.\n"));
350 					rv = KC_ERR_USAGE;
351 				} else {
352 					crl_set_attr++;
353 				}
354 				break;
355 			case 'u':
356 				plc.ku_bits = parseKUlist(optarg_av);
357 				if (plc.ku_bits == 0) {
358 					(void) fprintf(stderr, gettext(
359 					    "Error keyusage input.\n"));
360 					rv = KC_ERR_USAGE;
361 				}
362 				break;
363 			case 'E':
364 				if (parseEKUNames(optarg_av, &plc) != 0) {
365 					(void) fprintf(stderr,
366 					    gettext("Error EKU input.\n"));
367 					rv = KC_ERR_USAGE;
368 				}
369 				break;
370 			case 'O':
371 				if (parseEKUOIDs(optarg_av, &plc) != 0) {
372 					(void) fprintf(stderr,
373 					    gettext("Error EKU OID input.\n"));
374 					rv = KC_ERR_USAGE;
375 				}
376 				break;
377 			default:
378 				(void) fprintf(stderr,
379 				    gettext("Error input option.\n"));
380 				rv = KC_ERR_USAGE;
381 				break;
382 		}
383 
384 		if (rv != KC_OK)
385 			goto out;
386 	}
387 
388 	/* No additional args allowed. */
389 	argc -= optind_av;
390 	if (argc) {
391 		(void) fprintf(stderr,
392 		    gettext("Error input option\n"));
393 		rv = KC_ERR_USAGE;
394 		goto out;
395 	}
396 
397 	if (filename == NULL) {
398 		filename = strdup(KMF_DEFAULT_POLICY_FILE);
399 		if (filename == NULL) {
400 			rv = KC_ERR_MEMORY;
401 			goto out;
402 		}
403 	}
404 
405 	/*
406 	 * Must have a policy name. The policy name can not be default
407 	 * if using the default policy file.
408 	 */
409 	if (plc.name == NULL) {
410 		(void) fprintf(stderr,
411 		    gettext("You must specify a policy name\n"));
412 		rv = KC_ERR_USAGE;
413 		goto out;
414 	} else if (strcmp(filename, KMF_DEFAULT_POLICY_FILE) == 0 &&
415 	    strcmp(plc.name, KMF_DEFAULT_POLICY_NAME) == 0) {
416 		(void) fprintf(stderr,
417 		    gettext("Can not create a default policy in the default "
418 		    "policy file\n"));
419 		rv = KC_ERR_USAGE;
420 		goto out;
421 	}
422 
423 	/*
424 	 * If the policy file exists and the policy is in the policy file
425 	 * already, we will not create it again.
426 	 */
427 	if (access(filename, R_OK) == 0) {
428 		POLICY_LIST *plclist = NULL, *pnode;
429 		int found = 0;
430 
431 		rv = load_policies(filename, &plclist);
432 		if (rv != KMF_OK)
433 			goto out;
434 
435 		pnode = plclist;
436 		while (pnode != NULL && !found) {
437 			if (strcmp(plc.name, pnode->plc.name) == 0)
438 				found++;
439 			pnode = pnode->next;
440 		}
441 		free_policy_list(plclist);
442 
443 		if (found) {
444 			(void) fprintf(stderr,
445 			    gettext("Could not create policy \"%s\" - exists "
446 			    "already\n"), plc.name);
447 			rv = KC_ERR_USAGE;
448 			goto out;
449 		}
450 	}
451 
452 	/*
453 	 * If any OCSP attribute is set, turn on the OCSP checking flag.
454 	 * Also set "has_resp_cert" to be true, if the responder cert
455 	 * is provided.
456 	 */
457 	if (ocsp_set_attr > 0)
458 		plc.revocation |= KMF_REVOCATION_METHOD_OCSP;
459 
460 	if (plc.VAL_OCSP_RESP_CERT.name != NULL &&
461 	    plc.VAL_OCSP_RESP_CERT.serial != NULL) {
462 		plc.VAL_OCSP.has_resp_cert = B_TRUE;
463 	}
464 
465 	/*
466 	 * If any CRL attribute is set, turn on the CRL checking flag.
467 	 */
468 	if (crl_set_attr > 0)
469 		plc.revocation |= KMF_REVOCATION_METHOD_CRL;
470 
471 	/*
472 	 * Does a sanity check on the new policy.
473 	 */
474 	ret = KMF_VerifyPolicy(&plc);
475 	if (ret != KMF_OK) {
476 		print_sanity_error(ret);
477 		rv = KC_ERR_ADD_POLICY;
478 		goto out;
479 	}
480 
481 	/*
482 	 * Add to the DB.
483 	 */
484 	ret = KMF_AddPolicyToDB(&plc, filename, B_FALSE);
485 	if (ret != KMF_OK) {
486 		(void) fprintf(stderr,
487 		    gettext("Error adding policy to database: 0x%04x\n"), ret);
488 		rv = KC_ERR_ADD_POLICY;
489 	}
490 
491 out:
492 	if (filename != NULL)
493 		free(filename);
494 
495 	KMF_FreePolicyRecord(&plc);
496 
497 	return (rv);
498 }
499