1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate * CDDL HEADER START
3*0Sstevel@tonic-gate *
4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance
7*0Sstevel@tonic-gate * with the License.
8*0Sstevel@tonic-gate *
9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate * and limitations under the License.
13*0Sstevel@tonic-gate *
14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate *
20*0Sstevel@tonic-gate * CDDL HEADER END
21*0Sstevel@tonic-gate */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
24*0Sstevel@tonic-gate * Use is subject to license terms.
25*0Sstevel@tonic-gate */
26*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI"
27*0Sstevel@tonic-gate
28*0Sstevel@tonic-gate #include <stdio.h>
29*0Sstevel@tonic-gate #include <ctype.h>
30*0Sstevel@tonic-gate #include <unistd.h>
31*0Sstevel@tonic-gate #include <strings.h>
32*0Sstevel@tonic-gate #include <libintl.h>
33*0Sstevel@tonic-gate #include <locale.h>
34*0Sstevel@tonic-gate #include <limits.h>
35*0Sstevel@tonic-gate #include <libgen.h>
36*0Sstevel@tonic-gate #include <errno.h>
37*0Sstevel@tonic-gate #include <assert.h>
38*0Sstevel@tonic-gate #include <wanbootutil.h>
39*0Sstevel@tonic-gate #include <sys/sysmacros.h>
40*0Sstevel@tonic-gate #include <sys/socket.h>
41*0Sstevel@tonic-gate #include <sys/types.h>
42*0Sstevel@tonic-gate #include <sys/stat.h>
43*0Sstevel@tonic-gate #include <sys/wanboot_impl.h>
44*0Sstevel@tonic-gate #include <netinet/in.h>
45*0Sstevel@tonic-gate #include <arpa/inet.h>
46*0Sstevel@tonic-gate
47*0Sstevel@tonic-gate /* Return codes */
48*0Sstevel@tonic-gate #define KEYGEN_SUCCESS 0
49*0Sstevel@tonic-gate #define KEYGEN_ERROR 1
50*0Sstevel@tonic-gate
51*0Sstevel@tonic-gate /* Defaults */
52*0Sstevel@tonic-gate static char default_net[] = "0.0.0.0";
53*0Sstevel@tonic-gate static char default_cid[] = "00000000000000";
54*0Sstevel@tonic-gate
55*0Sstevel@tonic-gate /* Suboption. */
56*0Sstevel@tonic-gate #define NET 0
57*0Sstevel@tonic-gate #define CID 1
58*0Sstevel@tonic-gate #define TYPE 2
59*0Sstevel@tonic-gate
60*0Sstevel@tonic-gate static char *opts[] = { "net", "cid", "type", NULL };
61*0Sstevel@tonic-gate
62*0Sstevel@tonic-gate /*
63*0Sstevel@tonic-gate * This routine is used to parse the suboptions of '-o' option.
64*0Sstevel@tonic-gate *
65*0Sstevel@tonic-gate * The option should be of the form:
66*0Sstevel@tonic-gate * net=<addr>,cid=<cid>,type=<3des|aes|sha1|rsa>
67*0Sstevel@tonic-gate *
68*0Sstevel@tonic-gate * This routine will pass the values of each of the suboptions back in the
69*0Sstevel@tonic-gate * supplied arguments, 'net', 'cid' and 'ka'.
70*0Sstevel@tonic-gate *
71*0Sstevel@tonic-gate * Returns:
72*0Sstevel@tonic-gate * KEYGEN_SUCCESS or KEYGEN_ERROR.
73*0Sstevel@tonic-gate */
74*0Sstevel@tonic-gate static int
process_option(char * arg,char ** net,char ** cid,wbku_key_attr_t * ka)75*0Sstevel@tonic-gate process_option(char *arg, char **net, char **cid, wbku_key_attr_t *ka)
76*0Sstevel@tonic-gate {
77*0Sstevel@tonic-gate char *value;
78*0Sstevel@tonic-gate wbku_retcode_t ret;
79*0Sstevel@tonic-gate
80*0Sstevel@tonic-gate while (*arg != '\0') {
81*0Sstevel@tonic-gate switch (getsubopt(&arg, opts, &value)) {
82*0Sstevel@tonic-gate case NET:
83*0Sstevel@tonic-gate /*
84*0Sstevel@tonic-gate * Network number.
85*0Sstevel@tonic-gate */
86*0Sstevel@tonic-gate *net = value;
87*0Sstevel@tonic-gate break;
88*0Sstevel@tonic-gate case CID:
89*0Sstevel@tonic-gate /*
90*0Sstevel@tonic-gate * Client ID.
91*0Sstevel@tonic-gate */
92*0Sstevel@tonic-gate *cid = value;
93*0Sstevel@tonic-gate break;
94*0Sstevel@tonic-gate case TYPE:
95*0Sstevel@tonic-gate /*
96*0Sstevel@tonic-gate * Key type.
97*0Sstevel@tonic-gate */
98*0Sstevel@tonic-gate ret = wbku_str_to_keyattr(value, ka, WBKU_ANY_KEY);
99*0Sstevel@tonic-gate if (ret != WBKU_SUCCESS) {
100*0Sstevel@tonic-gate wbku_printerr("%s\n", wbku_retmsg(ret));
101*0Sstevel@tonic-gate return (KEYGEN_ERROR);
102*0Sstevel@tonic-gate }
103*0Sstevel@tonic-gate break;
104*0Sstevel@tonic-gate default:
105*0Sstevel@tonic-gate wbku_printerr("%s is not a valid option\n", value);
106*0Sstevel@tonic-gate return (KEYGEN_ERROR);
107*0Sstevel@tonic-gate }
108*0Sstevel@tonic-gate }
109*0Sstevel@tonic-gate
110*0Sstevel@tonic-gate /*
111*0Sstevel@tonic-gate * Sanity checks
112*0Sstevel@tonic-gate */
113*0Sstevel@tonic-gate if (*net != NULL && **net == '\0') {
114*0Sstevel@tonic-gate wbku_printerr("Missing net option value\n");
115*0Sstevel@tonic-gate return (KEYGEN_ERROR);
116*0Sstevel@tonic-gate }
117*0Sstevel@tonic-gate if (*cid != NULL && **cid == '\0') {
118*0Sstevel@tonic-gate wbku_printerr("Missing cid option value\n");
119*0Sstevel@tonic-gate return (KEYGEN_ERROR);
120*0Sstevel@tonic-gate }
121*0Sstevel@tonic-gate if (*cid != NULL && *net == NULL) {
122*0Sstevel@tonic-gate wbku_printerr(
123*0Sstevel@tonic-gate "The cid option requires net option specification\n");
124*0Sstevel@tonic-gate return (KEYGEN_ERROR);
125*0Sstevel@tonic-gate }
126*0Sstevel@tonic-gate if (ka->ka_type == WBKU_KEY_UNKNOWN) {
127*0Sstevel@tonic-gate wbku_printerr("Missing key type option value\n");
128*0Sstevel@tonic-gate return (KEYGEN_ERROR);
129*0Sstevel@tonic-gate }
130*0Sstevel@tonic-gate
131*0Sstevel@tonic-gate return (KEYGEN_SUCCESS);
132*0Sstevel@tonic-gate }
133*0Sstevel@tonic-gate
134*0Sstevel@tonic-gate /*
135*0Sstevel@tonic-gate * This routine parses a buffer to determine whether or not it
136*0Sstevel@tonic-gate * contains a hexascii string. If the buffer contains any characters
137*0Sstevel@tonic-gate * that are not hexascii, then it is not a hexascii string. Since
138*0Sstevel@tonic-gate * this function is used to validate a CID value (which is then used
139*0Sstevel@tonic-gate * to identify a directory in the filesystem), no evaluation of the
140*0Sstevel@tonic-gate * string is performed. That is, hex strings are not padded (e.g. "A"
141*0Sstevel@tonic-gate * is not padded to "0A").
142*0Sstevel@tonic-gate *
143*0Sstevel@tonic-gate * Returns:
144*0Sstevel@tonic-gate * B_TRUE or B_FALSE
145*0Sstevel@tonic-gate */
146*0Sstevel@tonic-gate static boolean_t
isxstring(const char * buf)147*0Sstevel@tonic-gate isxstring(const char *buf)
148*0Sstevel@tonic-gate {
149*0Sstevel@tonic-gate if ((strlen(buf) % 2) != 0) {
150*0Sstevel@tonic-gate return (B_FALSE);
151*0Sstevel@tonic-gate }
152*0Sstevel@tonic-gate
153*0Sstevel@tonic-gate for (; *buf != '\0'; ++buf) {
154*0Sstevel@tonic-gate if (!isxdigit(*buf)) {
155*0Sstevel@tonic-gate return (B_FALSE);
156*0Sstevel@tonic-gate }
157*0Sstevel@tonic-gate }
158*0Sstevel@tonic-gate return (B_TRUE);
159*0Sstevel@tonic-gate }
160*0Sstevel@tonic-gate
161*0Sstevel@tonic-gate /*
162*0Sstevel@tonic-gate * This routine uses the 'net' and the 'cid' to generate the client's
163*0Sstevel@tonic-gate * keystore filename and, if requested, creates the directory path to
164*0Sstevel@tonic-gate * the file if any of the directories do not exist. If directory path
165*0Sstevel@tonic-gate * creation is not requested and any of the directories do not exist,
166*0Sstevel@tonic-gate * then an error is returned.
167*0Sstevel@tonic-gate *
168*0Sstevel@tonic-gate * Returns:
169*0Sstevel@tonic-gate * KEYGEN_SUCCESS or KEYGEN_ERROR.
170*0Sstevel@tonic-gate */
171*0Sstevel@tonic-gate static int
create_client_filename(char * filename,size_t len,const char * net,const char * cid,boolean_t create)172*0Sstevel@tonic-gate create_client_filename(char *filename, size_t len, const char *net,
173*0Sstevel@tonic-gate const char *cid, boolean_t create)
174*0Sstevel@tonic-gate {
175*0Sstevel@tonic-gate struct in_addr addr;
176*0Sstevel@tonic-gate size_t size;
177*0Sstevel@tonic-gate
178*0Sstevel@tonic-gate if (net == NULL) {
179*0Sstevel@tonic-gate size = snprintf(filename, len, "%s", CLIENT_KEY_DIR);
180*0Sstevel@tonic-gate } else if (inet_pton(AF_INET, net, &addr) != 1) {
181*0Sstevel@tonic-gate wbku_printerr("%s is not a valid network address\n", net);
182*0Sstevel@tonic-gate return (KEYGEN_ERROR);
183*0Sstevel@tonic-gate } else if (cid == NULL) {
184*0Sstevel@tonic-gate size = snprintf(filename, len, "%s/%s", CLIENT_KEY_DIR, net);
185*0Sstevel@tonic-gate } else if (!isxstring(cid)) {
186*0Sstevel@tonic-gate wbku_printerr(
187*0Sstevel@tonic-gate "%s must be an even number of hexadecimal characters\n",
188*0Sstevel@tonic-gate cid);
189*0Sstevel@tonic-gate return (KEYGEN_ERROR);
190*0Sstevel@tonic-gate } else {
191*0Sstevel@tonic-gate size = snprintf(filename, len, "%s/%s/%s", CLIENT_KEY_DIR,
192*0Sstevel@tonic-gate net, cid);
193*0Sstevel@tonic-gate }
194*0Sstevel@tonic-gate
195*0Sstevel@tonic-gate /*
196*0Sstevel@tonic-gate * Shouldn't be a problem, but make sure buffer was big enough.
197*0Sstevel@tonic-gate */
198*0Sstevel@tonic-gate if (size >= len) {
199*0Sstevel@tonic-gate wbku_printerr("Keystore path too long\n");
200*0Sstevel@tonic-gate return (KEYGEN_ERROR);
201*0Sstevel@tonic-gate }
202*0Sstevel@tonic-gate
203*0Sstevel@tonic-gate /*
204*0Sstevel@tonic-gate * If directory creation is allowed, then try to create it.
205*0Sstevel@tonic-gate * If the directory already exists, then march on.
206*0Sstevel@tonic-gate */
207*0Sstevel@tonic-gate if (create) {
208*0Sstevel@tonic-gate if (mkdirp(filename, S_IRWXU) == -1 && errno != EEXIST) {
209*0Sstevel@tonic-gate wbku_printerr("Cannot create client keystore");
210*0Sstevel@tonic-gate return (KEYGEN_ERROR);
211*0Sstevel@tonic-gate }
212*0Sstevel@tonic-gate }
213*0Sstevel@tonic-gate
214*0Sstevel@tonic-gate /*
215*0Sstevel@tonic-gate * Append the filename.
216*0Sstevel@tonic-gate */
217*0Sstevel@tonic-gate if (strlcat(filename, "/keystore", len) >= len) {
218*0Sstevel@tonic-gate wbku_printerr("Keystore path too long\n");
219*0Sstevel@tonic-gate return (KEYGEN_ERROR);
220*0Sstevel@tonic-gate }
221*0Sstevel@tonic-gate
222*0Sstevel@tonic-gate return (KEYGEN_SUCCESS);
223*0Sstevel@tonic-gate }
224*0Sstevel@tonic-gate
225*0Sstevel@tonic-gate /*
226*0Sstevel@tonic-gate * This routine generates a random key of the type defined by 'ka'.
227*0Sstevel@tonic-gate * The key value is returned in 'rand_key' and the buffer pointed to
228*0Sstevel@tonic-gate * by 'rand_key' is assumed to be of the correct size.
229*0Sstevel@tonic-gate *
230*0Sstevel@tonic-gate * Note:
231*0Sstevel@tonic-gate * If 'ka' has a non-NULL keycheck value, then the routine will
232*0Sstevel@tonic-gate * generate randon keys until a non-weak key is generated.
233*0Sstevel@tonic-gate *
234*0Sstevel@tonic-gate * Returns:
235*0Sstevel@tonic-gate * KEYGEN_SUCCESS or KEYGEN_ERROR.
236*0Sstevel@tonic-gate */
237*0Sstevel@tonic-gate static int
gen_key(const wbku_key_attr_t * ka,uint8_t * rand_key)238*0Sstevel@tonic-gate gen_key(const wbku_key_attr_t *ka, uint8_t *rand_key)
239*0Sstevel@tonic-gate {
240*0Sstevel@tonic-gate /*
241*0Sstevel@tonic-gate * Generate key, until non-weak key generated.
242*0Sstevel@tonic-gate */
243*0Sstevel@tonic-gate for (;;) {
244*0Sstevel@tonic-gate if (wbio_nread_rand(rand_key, ka->ka_len) != 0) {
245*0Sstevel@tonic-gate wbku_printerr("Cannot generate random number");
246*0Sstevel@tonic-gate return (KEYGEN_ERROR);
247*0Sstevel@tonic-gate }
248*0Sstevel@tonic-gate
249*0Sstevel@tonic-gate if (ka->ka_keycheck == NULL || ka->ka_keycheck(rand_key)) {
250*0Sstevel@tonic-gate return (KEYGEN_SUCCESS);
251*0Sstevel@tonic-gate }
252*0Sstevel@tonic-gate }
253*0Sstevel@tonic-gate }
254*0Sstevel@tonic-gate
255*0Sstevel@tonic-gate /*
256*0Sstevel@tonic-gate * This routine generates a random master key of the type (currently only
257*0Sstevel@tonic-gate * HMAC SHA1 supported) defined by 'ka' and stores it in the master key
258*0Sstevel@tonic-gate * file.
259*0Sstevel@tonic-gate *
260*0Sstevel@tonic-gate * Returns:
261*0Sstevel@tonic-gate * KEYGEN_SUCCESS or KEYGEN_ERROR.
262*0Sstevel@tonic-gate */
263*0Sstevel@tonic-gate static int
master_gen_key(wbku_key_attr_t * ka)264*0Sstevel@tonic-gate master_gen_key(wbku_key_attr_t *ka)
265*0Sstevel@tonic-gate {
266*0Sstevel@tonic-gate uint8_t mas_key[WANBOOT_HMAC_KEY_SIZE];
267*0Sstevel@tonic-gate int fd;
268*0Sstevel@tonic-gate FILE *fp = NULL;
269*0Sstevel@tonic-gate fpos_t pos;
270*0Sstevel@tonic-gate wbku_retcode_t ret;
271*0Sstevel@tonic-gate boolean_t exists = B_FALSE;
272*0Sstevel@tonic-gate
273*0Sstevel@tonic-gate /*
274*0Sstevel@tonic-gate * If the file already exists (possibly via keymgmt), then open
275*0Sstevel@tonic-gate * the file for update. Otherwise create it and open it for
276*0Sstevel@tonic-gate * for writing.
277*0Sstevel@tonic-gate */
278*0Sstevel@tonic-gate fd = open(MASTER_KEY_FILE, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
279*0Sstevel@tonic-gate if (fd < 0) {
280*0Sstevel@tonic-gate if (errno == EEXIST) {
281*0Sstevel@tonic-gate fp = fopen(MASTER_KEY_FILE, "r+");
282*0Sstevel@tonic-gate exists = B_TRUE;
283*0Sstevel@tonic-gate }
284*0Sstevel@tonic-gate } else {
285*0Sstevel@tonic-gate if ((fp = fdopen(fd, "w")) == NULL) {
286*0Sstevel@tonic-gate (void) close(fd);
287*0Sstevel@tonic-gate }
288*0Sstevel@tonic-gate }
289*0Sstevel@tonic-gate
290*0Sstevel@tonic-gate if (fp == NULL) {
291*0Sstevel@tonic-gate wbku_printerr("Cannot open master keystore", MASTER_KEY_FILE);
292*0Sstevel@tonic-gate return (KEYGEN_ERROR);
293*0Sstevel@tonic-gate }
294*0Sstevel@tonic-gate
295*0Sstevel@tonic-gate /*
296*0Sstevel@tonic-gate * If the file already exists, then see if a master key already
297*0Sstevel@tonic-gate * exists. We will not overwrite it if it does.
298*0Sstevel@tonic-gate */
299*0Sstevel@tonic-gate ret = WBKU_NOKEY;
300*0Sstevel@tonic-gate if (exists) {
301*0Sstevel@tonic-gate ret = wbku_find_key(fp, NULL, ka, NULL, B_TRUE);
302*0Sstevel@tonic-gate if (ret != WBKU_NOKEY) {
303*0Sstevel@tonic-gate if (ret == WBKU_SUCCESS) {
304*0Sstevel@tonic-gate wbku_printerr("The master %s key already "
305*0Sstevel@tonic-gate "exists and will not be overwritten\n",
306*0Sstevel@tonic-gate ka->ka_str);
307*0Sstevel@tonic-gate } else {
308*0Sstevel@tonic-gate wbku_printerr("%s\n", wbku_retmsg(ret));
309*0Sstevel@tonic-gate }
310*0Sstevel@tonic-gate (void) fclose(fp);
311*0Sstevel@tonic-gate return (KEYGEN_ERROR);
312*0Sstevel@tonic-gate }
313*0Sstevel@tonic-gate }
314*0Sstevel@tonic-gate
315*0Sstevel@tonic-gate /*
316*0Sstevel@tonic-gate * If wbku_find_key() did not find the key position for us
317*0Sstevel@tonic-gate * (expected behavior), then we should set position to
318*0Sstevel@tonic-gate * the end of the file.
319*0Sstevel@tonic-gate */
320*0Sstevel@tonic-gate if (ret == WBKU_NOKEY &&
321*0Sstevel@tonic-gate (fseek(fp, 0, SEEK_END) != 0 || fgetpos(fp, &pos) != 0)) {
322*0Sstevel@tonic-gate wbku_printerr("Internal error");
323*0Sstevel@tonic-gate (void) fclose(fp);
324*0Sstevel@tonic-gate return (KEYGEN_ERROR);
325*0Sstevel@tonic-gate }
326*0Sstevel@tonic-gate
327*0Sstevel@tonic-gate /*
328*0Sstevel@tonic-gate * Generate a key and write it.
329*0Sstevel@tonic-gate */
330*0Sstevel@tonic-gate if (gen_key(ka, mas_key) != KEYGEN_SUCCESS) {
331*0Sstevel@tonic-gate (void) fclose(fp);
332*0Sstevel@tonic-gate return (KEYGEN_ERROR);
333*0Sstevel@tonic-gate }
334*0Sstevel@tonic-gate
335*0Sstevel@tonic-gate ret = wbku_write_key(fp, &pos, ka, mas_key, B_TRUE);
336*0Sstevel@tonic-gate (void) fclose(fp);
337*0Sstevel@tonic-gate if (ret != WBKU_SUCCESS) {
338*0Sstevel@tonic-gate wbku_printerr("%s\n", wbku_retmsg(ret));
339*0Sstevel@tonic-gate return (KEYGEN_ERROR);
340*0Sstevel@tonic-gate }
341*0Sstevel@tonic-gate
342*0Sstevel@tonic-gate (void) printf(gettext("The master %s key has been generated\n"),
343*0Sstevel@tonic-gate ka->ka_str);
344*0Sstevel@tonic-gate return (KEYGEN_SUCCESS);
345*0Sstevel@tonic-gate }
346*0Sstevel@tonic-gate
347*0Sstevel@tonic-gate /*
348*0Sstevel@tonic-gate * This routine generates a random client key of the type
349*0Sstevel@tonic-gate * defined by 'ka' and stores it in the client keystore.
350*0Sstevel@tonic-gate * file.
351*0Sstevel@tonic-gate *
352*0Sstevel@tonic-gate * Returns:
353*0Sstevel@tonic-gate * KEYGEN_SUCCESS or KEYGEN_ERROR.
354*0Sstevel@tonic-gate */
355*0Sstevel@tonic-gate static int
client_gen_key(const char * filename,wbku_key_attr_t * ka,const char * net,const char * cid)356*0Sstevel@tonic-gate client_gen_key(const char *filename, wbku_key_attr_t *ka, const char *net,
357*0Sstevel@tonic-gate const char *cid)
358*0Sstevel@tonic-gate {
359*0Sstevel@tonic-gate int fd;
360*0Sstevel@tonic-gate FILE *cli_fp = NULL;
361*0Sstevel@tonic-gate FILE *mas_fp;
362*0Sstevel@tonic-gate fpos_t pos;
363*0Sstevel@tonic-gate uint8_t cli_key[WANBOOT_MAXKEYLEN];
364*0Sstevel@tonic-gate uint8_t mas_key[WANBOOT_HMAC_KEY_SIZE];
365*0Sstevel@tonic-gate SHA1_CTX ctx;
366*0Sstevel@tonic-gate char cid_buf[PATH_MAX];
367*0Sstevel@tonic-gate boolean_t exists = B_FALSE;
368*0Sstevel@tonic-gate wbku_retcode_t ret;
369*0Sstevel@tonic-gate
370*0Sstevel@tonic-gate /*
371*0Sstevel@tonic-gate * If the file already exists (possibly via keymgmt), then open
372*0Sstevel@tonic-gate * the file for update. Otherwise create it and open it for
373*0Sstevel@tonic-gate * for writing.
374*0Sstevel@tonic-gate */
375*0Sstevel@tonic-gate fd = open(filename, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
376*0Sstevel@tonic-gate if (fd < 0) {
377*0Sstevel@tonic-gate if (errno == EEXIST) {
378*0Sstevel@tonic-gate cli_fp = fopen(filename, "r+");
379*0Sstevel@tonic-gate exists = B_TRUE;
380*0Sstevel@tonic-gate }
381*0Sstevel@tonic-gate } else {
382*0Sstevel@tonic-gate if ((cli_fp = fdopen(fd, "w")) == NULL) {
383*0Sstevel@tonic-gate (void) close(fd);
384*0Sstevel@tonic-gate }
385*0Sstevel@tonic-gate }
386*0Sstevel@tonic-gate
387*0Sstevel@tonic-gate if (cli_fp == NULL) {
388*0Sstevel@tonic-gate wbku_printerr("Cannot open client keystore");
389*0Sstevel@tonic-gate return (KEYGEN_ERROR);
390*0Sstevel@tonic-gate }
391*0Sstevel@tonic-gate
392*0Sstevel@tonic-gate /*
393*0Sstevel@tonic-gate * Generate the key. Encryption keys can be generated by simply
394*0Sstevel@tonic-gate * calling gen_key(). An HMAC SHA1 key will be generated by
395*0Sstevel@tonic-gate * hashing the master key.
396*0Sstevel@tonic-gate */
397*0Sstevel@tonic-gate switch (ka->ka_type) {
398*0Sstevel@tonic-gate case WBKU_KEY_3DES:
399*0Sstevel@tonic-gate case WBKU_KEY_AES_128:
400*0Sstevel@tonic-gate if (gen_key(ka, cli_key) != KEYGEN_SUCCESS) {
401*0Sstevel@tonic-gate (void) fclose(cli_fp);
402*0Sstevel@tonic-gate return (KEYGEN_ERROR);
403*0Sstevel@tonic-gate }
404*0Sstevel@tonic-gate break;
405*0Sstevel@tonic-gate case WBKU_KEY_HMAC_SHA1:
406*0Sstevel@tonic-gate /*
407*0Sstevel@tonic-gate * Follow RFC 3118 Appendix A's algorithm to generate
408*0Sstevel@tonic-gate * the HMAC/SHA1 client key.
409*0Sstevel@tonic-gate */
410*0Sstevel@tonic-gate
411*0Sstevel@tonic-gate /*
412*0Sstevel@tonic-gate * Open the master keystore for reading only.
413*0Sstevel@tonic-gate */
414*0Sstevel@tonic-gate if ((mas_fp = fopen(MASTER_KEY_FILE, "r")) == NULL) {
415*0Sstevel@tonic-gate wbku_printerr("Cannot open master keystore");
416*0Sstevel@tonic-gate (void) fclose(cli_fp);
417*0Sstevel@tonic-gate return (KEYGEN_ERROR);
418*0Sstevel@tonic-gate }
419*0Sstevel@tonic-gate
420*0Sstevel@tonic-gate /*
421*0Sstevel@tonic-gate * Find the master key.
422*0Sstevel@tonic-gate */
423*0Sstevel@tonic-gate ret = wbku_find_key(mas_fp, NULL, ka, mas_key, B_TRUE);
424*0Sstevel@tonic-gate if (ret != WBKU_SUCCESS) {
425*0Sstevel@tonic-gate if (ret == WBKU_NOKEY) {
426*0Sstevel@tonic-gate wbku_printerr("Cannot create a client key "
427*0Sstevel@tonic-gate "without first creating a master key\n");
428*0Sstevel@tonic-gate } else {
429*0Sstevel@tonic-gate wbku_printerr("%s\n", wbku_retmsg(ret));
430*0Sstevel@tonic-gate }
431*0Sstevel@tonic-gate (void) fclose(mas_fp);
432*0Sstevel@tonic-gate (void) fclose(cli_fp);
433*0Sstevel@tonic-gate return (KEYGEN_ERROR);
434*0Sstevel@tonic-gate }
435*0Sstevel@tonic-gate (void) fclose(mas_fp);
436*0Sstevel@tonic-gate
437*0Sstevel@tonic-gate /*
438*0Sstevel@tonic-gate * Now generate the client's unique ID buffer.
439*0Sstevel@tonic-gate */
440*0Sstevel@tonic-gate if (strlcpy(cid_buf, net, PATH_MAX) >= PATH_MAX ||
441*0Sstevel@tonic-gate strlcat(cid_buf, cid, PATH_MAX) >= PATH_MAX) {
442*0Sstevel@tonic-gate wbku_printerr("Unique id for client is too big\n");
443*0Sstevel@tonic-gate (void) fclose(cli_fp);
444*0Sstevel@tonic-gate return (KEYGEN_ERROR);
445*0Sstevel@tonic-gate }
446*0Sstevel@tonic-gate
447*0Sstevel@tonic-gate /*
448*0Sstevel@tonic-gate * Hash the buffer to create the client key.
449*0Sstevel@tonic-gate */
450*0Sstevel@tonic-gate HMACInit(&ctx, mas_key, WANBOOT_HMAC_KEY_SIZE);
451*0Sstevel@tonic-gate HMACUpdate(&ctx, (uint8_t *)cid_buf, strlen(cid_buf));
452*0Sstevel@tonic-gate HMACFinal(&ctx, mas_key, WANBOOT_HMAC_KEY_SIZE, cli_key);
453*0Sstevel@tonic-gate
454*0Sstevel@tonic-gate break;
455*0Sstevel@tonic-gate case WBKU_KEY_RSA:
456*0Sstevel@tonic-gate wbku_printerr("Cannot generate RSA key using keygen\n");
457*0Sstevel@tonic-gate (void) fclose(cli_fp);
458*0Sstevel@tonic-gate return (KEYGEN_ERROR);
459*0Sstevel@tonic-gate default:
460*0Sstevel@tonic-gate wbku_printerr("Internal error\n");
461*0Sstevel@tonic-gate (void) fclose(cli_fp);
462*0Sstevel@tonic-gate return (KEYGEN_ERROR);
463*0Sstevel@tonic-gate }
464*0Sstevel@tonic-gate
465*0Sstevel@tonic-gate /*
466*0Sstevel@tonic-gate * Look to see if a client key of this type exists and if
467*0Sstevel@tonic-gate * it does note its position in the file.
468*0Sstevel@tonic-gate */
469*0Sstevel@tonic-gate ret = WBKU_NOKEY;
470*0Sstevel@tonic-gate if (exists) {
471*0Sstevel@tonic-gate ret = wbku_find_key(cli_fp, &pos, ka, NULL, B_FALSE);
472*0Sstevel@tonic-gate if (ret != WBKU_SUCCESS && ret != WBKU_NOKEY) {
473*0Sstevel@tonic-gate wbku_printerr("%s\n", wbku_retmsg(ret));
474*0Sstevel@tonic-gate (void) fclose(cli_fp);
475*0Sstevel@tonic-gate return (KEYGEN_ERROR);
476*0Sstevel@tonic-gate }
477*0Sstevel@tonic-gate }
478*0Sstevel@tonic-gate
479*0Sstevel@tonic-gate /*
480*0Sstevel@tonic-gate * If wbku_find_key() did not find the key position for us,
481*0Sstevel@tonic-gate * then we should set position to the end of the file.
482*0Sstevel@tonic-gate */
483*0Sstevel@tonic-gate if (ret == WBKU_NOKEY &&
484*0Sstevel@tonic-gate (fseek(cli_fp, 0, SEEK_END) != 0 || fgetpos(cli_fp, &pos) != 0)) {
485*0Sstevel@tonic-gate wbku_printerr("Internal error");
486*0Sstevel@tonic-gate (void) fclose(cli_fp);
487*0Sstevel@tonic-gate return (KEYGEN_ERROR);
488*0Sstevel@tonic-gate }
489*0Sstevel@tonic-gate
490*0Sstevel@tonic-gate /*
491*0Sstevel@tonic-gate * Write the key.
492*0Sstevel@tonic-gate */
493*0Sstevel@tonic-gate ret = wbku_write_key(cli_fp, &pos, ka, cli_key, B_FALSE);
494*0Sstevel@tonic-gate if (ret != WBKU_SUCCESS) {
495*0Sstevel@tonic-gate wbku_printerr("%s\n", wbku_retmsg(ret));
496*0Sstevel@tonic-gate (void) fclose(cli_fp);
497*0Sstevel@tonic-gate return (KEYGEN_ERROR);
498*0Sstevel@tonic-gate }
499*0Sstevel@tonic-gate (void) fclose(cli_fp);
500*0Sstevel@tonic-gate
501*0Sstevel@tonic-gate (void) printf(gettext("A new client %s key has been generated\n"),
502*0Sstevel@tonic-gate ka->ka_str);
503*0Sstevel@tonic-gate
504*0Sstevel@tonic-gate return (KEYGEN_SUCCESS);
505*0Sstevel@tonic-gate }
506*0Sstevel@tonic-gate
507*0Sstevel@tonic-gate /*
508*0Sstevel@tonic-gate * This routine is used to print a hexascii version of a key.
509*0Sstevel@tonic-gate * The hexascii version of the key will be twice the length
510*0Sstevel@tonic-gate * of 'datalen'.
511*0Sstevel@tonic-gate */
512*0Sstevel@tonic-gate static void
keydump(const char * key,int keylen)513*0Sstevel@tonic-gate keydump(const char *key, int keylen)
514*0Sstevel@tonic-gate {
515*0Sstevel@tonic-gate uint16_t *p16;
516*0Sstevel@tonic-gate
517*0Sstevel@tonic-gate assert(IS_P2ALIGNED(key, sizeof (uint16_t)));
518*0Sstevel@tonic-gate /*LINTED aligned*/
519*0Sstevel@tonic-gate for (p16 = (uint16_t *)key; keylen > 0; keylen -= 2) {
520*0Sstevel@tonic-gate (void) printf("%04x", htons(*p16++));
521*0Sstevel@tonic-gate }
522*0Sstevel@tonic-gate (void) printf("\n");
523*0Sstevel@tonic-gate }
524*0Sstevel@tonic-gate
525*0Sstevel@tonic-gate /*
526*0Sstevel@tonic-gate * This routine is used to print a key of the type
527*0Sstevel@tonic-gate * described by 'ka'. If 'master' is true, then the
528*0Sstevel@tonic-gate * key to display is the master key. Otherwise, it's a
529*0Sstevel@tonic-gate * client key.
530*0Sstevel@tonic-gate *
531*0Sstevel@tonic-gate * Returns:
532*0Sstevel@tonic-gate * KEYGEN_SUCCESS or KEYGEN_ERROR.
533*0Sstevel@tonic-gate */
534*0Sstevel@tonic-gate static int
display_key(const char * filename,wbku_key_attr_t * ka,boolean_t master)535*0Sstevel@tonic-gate display_key(const char *filename, wbku_key_attr_t *ka, boolean_t master)
536*0Sstevel@tonic-gate {
537*0Sstevel@tonic-gate uint8_t key[WANBOOT_MAXKEYLEN];
538*0Sstevel@tonic-gate FILE *fp;
539*0Sstevel@tonic-gate wbku_retcode_t ret;
540*0Sstevel@tonic-gate
541*0Sstevel@tonic-gate /*
542*0Sstevel@tonic-gate * Open the keystore for reading only.
543*0Sstevel@tonic-gate */
544*0Sstevel@tonic-gate if ((fp = fopen(filename, "r")) == NULL) {
545*0Sstevel@tonic-gate wbku_printerr("Cannot open keystore");
546*0Sstevel@tonic-gate return (KEYGEN_ERROR);
547*0Sstevel@tonic-gate }
548*0Sstevel@tonic-gate
549*0Sstevel@tonic-gate /*
550*0Sstevel@tonic-gate * Find the key.
551*0Sstevel@tonic-gate */
552*0Sstevel@tonic-gate ret = wbku_find_key(fp, NULL, ka, key, master);
553*0Sstevel@tonic-gate if (ret != WBKU_SUCCESS) {
554*0Sstevel@tonic-gate if (ret == WBKU_NOKEY) {
555*0Sstevel@tonic-gate wbku_printerr("The %s %s key does not exist\n",
556*0Sstevel@tonic-gate (master ? "master" : "client"), ka->ka_str);
557*0Sstevel@tonic-gate } else {
558*0Sstevel@tonic-gate wbku_printerr("%s\n", wbku_retmsg(ret));
559*0Sstevel@tonic-gate }
560*0Sstevel@tonic-gate (void) fclose(fp);
561*0Sstevel@tonic-gate return (KEYGEN_ERROR);
562*0Sstevel@tonic-gate }
563*0Sstevel@tonic-gate (void) fclose(fp);
564*0Sstevel@tonic-gate
565*0Sstevel@tonic-gate /*
566*0Sstevel@tonic-gate * Dump the key in hex.
567*0Sstevel@tonic-gate */
568*0Sstevel@tonic-gate keydump((char *)key, ka->ka_len);
569*0Sstevel@tonic-gate
570*0Sstevel@tonic-gate return (KEYGEN_SUCCESS);
571*0Sstevel@tonic-gate }
572*0Sstevel@tonic-gate
573*0Sstevel@tonic-gate /*
574*0Sstevel@tonic-gate * Prints usage().
575*0Sstevel@tonic-gate */
576*0Sstevel@tonic-gate static void
usage(const char * cmd)577*0Sstevel@tonic-gate usage(const char *cmd)
578*0Sstevel@tonic-gate {
579*0Sstevel@tonic-gate (void) fprintf(stderr, gettext("Usage: %s [-m | -c "
580*0Sstevel@tonic-gate "-o net=<addr>,cid=<cid>,type=<%s|%s|%s>]\n"
581*0Sstevel@tonic-gate " %s -d [-m | -c -o net=<addr>,cid=<cid>,"
582*0Sstevel@tonic-gate "type=<%s|%s|%s|%s>]\n"),
583*0Sstevel@tonic-gate cmd, WBKU_KW_3DES, WBKU_KW_AES_128, WBKU_KW_HMAC_SHA1,
584*0Sstevel@tonic-gate cmd, WBKU_KW_3DES, WBKU_KW_AES_128, WBKU_KW_HMAC_SHA1, WBKU_KW_RSA);
585*0Sstevel@tonic-gate }
586*0Sstevel@tonic-gate
587*0Sstevel@tonic-gate /*
588*0Sstevel@tonic-gate * This program is used to generate and display WAN boot encryption and
589*0Sstevel@tonic-gate * hash keys. The paths to the keystores are predetermined. That is, the
590*0Sstevel@tonic-gate * master keystore (used to store a master HMAC SHA1 key) will always
591*0Sstevel@tonic-gate * reside in the default location, MASTER_KEY_FILE. The client keystores
592*0Sstevel@tonic-gate * will always reside in default locations that are computed using their
593*0Sstevel@tonic-gate * network number and cid values.
594*0Sstevel@tonic-gate *
595*0Sstevel@tonic-gate * Note:
596*0Sstevel@tonic-gate * The master keystore can store client keys too. This program
597*0Sstevel@tonic-gate * cannot be used to insert client keys into the master keystore.
598*0Sstevel@tonic-gate * However, it must not corrupt any client keystore inserted into
599*0Sstevel@tonic-gate * the file by other means (keymgmt).
600*0Sstevel@tonic-gate *
601*0Sstevel@tonic-gate * We do not do any file locking scheme. This means that if two
602*0Sstevel@tonic-gate * keygen commands are run concurrently, results can be disastrous.
603*0Sstevel@tonic-gate *
604*0Sstevel@tonic-gate * Returns:
605*0Sstevel@tonic-gate * KEYGEN_SUCCESS or KEYGEN_ERROR.
606*0Sstevel@tonic-gate */
607*0Sstevel@tonic-gate int
main(int argc,char ** argv)608*0Sstevel@tonic-gate main(int argc, char **argv)
609*0Sstevel@tonic-gate {
610*0Sstevel@tonic-gate char filename[PATH_MAX];
611*0Sstevel@tonic-gate char *filenamep;
612*0Sstevel@tonic-gate int c;
613*0Sstevel@tonic-gate boolean_t is_client = B_FALSE;
614*0Sstevel@tonic-gate boolean_t is_master = B_FALSE;
615*0Sstevel@tonic-gate boolean_t display = B_FALSE;
616*0Sstevel@tonic-gate char *net = NULL;
617*0Sstevel@tonic-gate char *cid = NULL;
618*0Sstevel@tonic-gate wbku_key_attr_t ka;
619*0Sstevel@tonic-gate wbku_retcode_t ret;
620*0Sstevel@tonic-gate
621*0Sstevel@tonic-gate /*
622*0Sstevel@tonic-gate * Do the necessary magic for localization support.
623*0Sstevel@tonic-gate */
624*0Sstevel@tonic-gate (void) setlocale(LC_ALL, "");
625*0Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)
626*0Sstevel@tonic-gate #define TEXT_DOMAIN "SYS_TEST"
627*0Sstevel@tonic-gate #endif
628*0Sstevel@tonic-gate (void) textdomain(TEXT_DOMAIN);
629*0Sstevel@tonic-gate
630*0Sstevel@tonic-gate /*
631*0Sstevel@tonic-gate * Initialize program name for use by wbku_printerr().
632*0Sstevel@tonic-gate */
633*0Sstevel@tonic-gate wbku_errinit(argv[0]);
634*0Sstevel@tonic-gate
635*0Sstevel@tonic-gate /*
636*0Sstevel@tonic-gate * At the very least, we'll need one arg.
637*0Sstevel@tonic-gate */
638*0Sstevel@tonic-gate if (argc < 2) {
639*0Sstevel@tonic-gate usage(argv[0]);
640*0Sstevel@tonic-gate return (KEYGEN_ERROR);
641*0Sstevel@tonic-gate }
642*0Sstevel@tonic-gate
643*0Sstevel@tonic-gate /*
644*0Sstevel@tonic-gate * Parse the options.
645*0Sstevel@tonic-gate */
646*0Sstevel@tonic-gate ka.ka_type = WBKU_KEY_UNKNOWN;
647*0Sstevel@tonic-gate while ((c = getopt(argc, argv, "dcmo:")) != EOF) {
648*0Sstevel@tonic-gate switch (c) {
649*0Sstevel@tonic-gate case 'd':
650*0Sstevel@tonic-gate /*
651*0Sstevel@tonic-gate * Display a key.
652*0Sstevel@tonic-gate */
653*0Sstevel@tonic-gate display = B_TRUE;
654*0Sstevel@tonic-gate break;
655*0Sstevel@tonic-gate case 'o':
656*0Sstevel@tonic-gate /*
657*0Sstevel@tonic-gate * Suboptions.
658*0Sstevel@tonic-gate */
659*0Sstevel@tonic-gate if (process_option(optarg, &net, &cid, &ka) != 0) {
660*0Sstevel@tonic-gate usage(argv[0]);
661*0Sstevel@tonic-gate return (KEYGEN_ERROR);
662*0Sstevel@tonic-gate }
663*0Sstevel@tonic-gate break;
664*0Sstevel@tonic-gate case 'c':
665*0Sstevel@tonic-gate is_client = B_TRUE;
666*0Sstevel@tonic-gate break;
667*0Sstevel@tonic-gate case 'm':
668*0Sstevel@tonic-gate is_master = B_TRUE;
669*0Sstevel@tonic-gate break;
670*0Sstevel@tonic-gate default:
671*0Sstevel@tonic-gate usage(argv[0]);
672*0Sstevel@tonic-gate return (KEYGEN_ERROR);
673*0Sstevel@tonic-gate }
674*0Sstevel@tonic-gate }
675*0Sstevel@tonic-gate
676*0Sstevel@tonic-gate /*
677*0Sstevel@tonic-gate * Must be operating on a master or client key and if
678*0Sstevel@tonic-gate * it's a client key, then type must have been given.
679*0Sstevel@tonic-gate */
680*0Sstevel@tonic-gate if ((is_client == is_master) ||
681*0Sstevel@tonic-gate (is_client && ka.ka_type == WBKU_KEY_UNKNOWN)) {
682*0Sstevel@tonic-gate usage(argv[0]);
683*0Sstevel@tonic-gate return (KEYGEN_ERROR);
684*0Sstevel@tonic-gate }
685*0Sstevel@tonic-gate
686*0Sstevel@tonic-gate /*
687*0Sstevel@tonic-gate * If operating on the master key, then it is an HMAC SHA1
688*0Sstevel@tonic-gate * key. Build the correct 'ka'. If we're working on a client
689*0Sstevel@tonic-gate * key, the 'ka' was already built as part of option parsing.
690*0Sstevel@tonic-gate */
691*0Sstevel@tonic-gate if (is_master) {
692*0Sstevel@tonic-gate ret = wbku_str_to_keyattr(WBKU_KW_HMAC_SHA1, &ka,
693*0Sstevel@tonic-gate WBKU_HASH_KEY);
694*0Sstevel@tonic-gate if (ret != WBKU_SUCCESS) {
695*0Sstevel@tonic-gate wbku_printerr("Internal error\n");
696*0Sstevel@tonic-gate return (KEYGEN_ERROR);
697*0Sstevel@tonic-gate }
698*0Sstevel@tonic-gate filenamep = MASTER_KEY_FILE;
699*0Sstevel@tonic-gate } else {
700*0Sstevel@tonic-gate /*
701*0Sstevel@tonic-gate * Build the path to the client keystore.
702*0Sstevel@tonic-gate */
703*0Sstevel@tonic-gate if (create_client_filename(filename, sizeof (filename), net,
704*0Sstevel@tonic-gate cid, !display) != KEYGEN_SUCCESS) {
705*0Sstevel@tonic-gate return (KEYGEN_ERROR);
706*0Sstevel@tonic-gate }
707*0Sstevel@tonic-gate filenamep = filename;
708*0Sstevel@tonic-gate }
709*0Sstevel@tonic-gate
710*0Sstevel@tonic-gate /*
711*0Sstevel@tonic-gate * If display chosen, go do it.
712*0Sstevel@tonic-gate */
713*0Sstevel@tonic-gate if (display) {
714*0Sstevel@tonic-gate return (display_key(filenamep, &ka, is_master));
715*0Sstevel@tonic-gate }
716*0Sstevel@tonic-gate
717*0Sstevel@tonic-gate /*
718*0Sstevel@tonic-gate * Can't generate RSA key here.
719*0Sstevel@tonic-gate */
720*0Sstevel@tonic-gate if (ka.ka_type == WBKU_KEY_RSA) {
721*0Sstevel@tonic-gate wbku_printerr("keygen cannot create RSA key\n");
722*0Sstevel@tonic-gate return (KEYGEN_ERROR);
723*0Sstevel@tonic-gate }
724*0Sstevel@tonic-gate
725*0Sstevel@tonic-gate /*
726*0Sstevel@tonic-gate * If generating a master key, go do it.
727*0Sstevel@tonic-gate */
728*0Sstevel@tonic-gate if (is_master) {
729*0Sstevel@tonic-gate return (master_gen_key(&ka));
730*0Sstevel@tonic-gate }
731*0Sstevel@tonic-gate
732*0Sstevel@tonic-gate /*
733*0Sstevel@tonic-gate * Must be generating a client key, go do it.
734*0Sstevel@tonic-gate */
735*0Sstevel@tonic-gate if (net == NULL) {
736*0Sstevel@tonic-gate net = default_net;
737*0Sstevel@tonic-gate }
738*0Sstevel@tonic-gate if (cid == NULL) {
739*0Sstevel@tonic-gate cid = default_cid;
740*0Sstevel@tonic-gate }
741*0Sstevel@tonic-gate if (client_gen_key(filename, &ka, net, cid) != KEYGEN_SUCCESS) {
742*0Sstevel@tonic-gate return (KEYGEN_ERROR);
743*0Sstevel@tonic-gate }
744*0Sstevel@tonic-gate
745*0Sstevel@tonic-gate return (KEYGEN_SUCCESS);
746*0Sstevel@tonic-gate }
747