xref: /illumos-gate/usr/src/lib/libdladm/common/secobj.c (revision 8548bf79039833dba8615afdf63258b2cb122121)
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 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <strings.h>
31 #include <errno.h>
32 #include <ctype.h>
33 #include <fcntl.h>
34 #include <sys/stat.h>
35 #include <sys/dld.h>
36 #include <libinetutil.h>
37 #include <libdllink.h>
38 #include <libdladm_impl.h>
39 
40 static dladm_status_t	i_dladm_set_secobj_db(const char *,
41 			    dladm_secobj_class_t, uint8_t *, uint_t);
42 static dladm_status_t	i_dladm_get_secobj_db(const char *,
43 			    dladm_secobj_class_t *, uint8_t *, uint_t *);
44 static dladm_status_t	i_dladm_unset_secobj_db(const char *);
45 static dladm_status_t	i_dladm_walk_secobj_db(void *,
46 			    boolean_t (*)(void *, const char *));
47 
48 typedef struct secobj_class_info {
49 	const char		*sc_name;
50 	dld_secobj_class_t	sc_dldclass;
51 } secobj_class_info_t;
52 
53 static secobj_class_info_t secobj_class_table[] = {
54 	{"wep",	DLD_SECOBJ_CLASS_WEP}
55 };
56 
57 #define	NSECOBJCLASS \
58 	(sizeof (secobj_class_table) / sizeof (secobj_class_info_t))
59 
60 static boolean_t
61 dladm_check_secobjclass(dladm_secobj_class_t class)
62 {
63 	return (class >= 0 && class < NSECOBJCLASS);
64 }
65 
66 dladm_status_t
67 dladm_str2secobjclass(const char *str, dladm_secobj_class_t *class)
68 {
69 	int			i;
70 	secobj_class_info_t	*sp;
71 
72 	for (i = 0; i < NSECOBJCLASS; i++) {
73 		sp = &secobj_class_table[i];
74 		if (strcasecmp(str, sp->sc_name) == 0) {
75 			*class = i;
76 			return (DLADM_STATUS_OK);
77 		}
78 	}
79 	return (DLADM_STATUS_BADARG);
80 }
81 
82 const char *
83 dladm_secobjclass2str(dladm_secobj_class_t class, char *buf)
84 {
85 	const char		*s;
86 
87 	if (!dladm_check_secobjclass(class))
88 		s = "";
89 	else
90 		s = secobj_class_table[class].sc_name;
91 
92 	(void) snprintf(buf, DLADM_STRSIZE, "%s", s);
93 	return (buf);
94 }
95 
96 static boolean_t
97 dladm_convert_secobjclass(dladm_secobj_class_t class,
98     dld_secobj_class_t *dldclass)
99 {
100 	if (!dladm_check_secobjclass(class))
101 		return (B_FALSE);
102 
103 	*dldclass = secobj_class_table[class].sc_dldclass;
104 	return (B_TRUE);
105 }
106 
107 static boolean_t
108 dladm_convert_dldsecobjclass(dld_secobj_class_t dldclass,
109     dladm_secobj_class_t *class)
110 {
111 	int			i;
112 	secobj_class_info_t	*sp;
113 
114 	for (i = 0; i < NSECOBJCLASS; i++) {
115 		sp = &secobj_class_table[i];
116 		if (dldclass == sp->sc_dldclass) {
117 			*class = i;
118 			return (B_TRUE);
119 		}
120 	}
121 	return (B_FALSE);
122 }
123 
124 dladm_status_t
125 dladm_set_secobj(const char *obj_name, dladm_secobj_class_t class,
126     uint8_t *obj_val, uint_t obj_len, uint_t flags)
127 {
128 	int			fd;
129 	dladm_status_t		status = DLADM_STATUS_OK;
130 	dld_ioc_secobj_set_t	secobj_set;
131 	dld_secobj_t		*objp;
132 
133 	if (!dladm_check_secobjclass(class) || flags == 0 ||
134 	    obj_name == NULL || strlen(obj_name) > DLD_SECOBJ_NAME_MAX ||
135 	    obj_val == NULL || obj_len == 0 || obj_len > DLD_SECOBJ_VAL_MAX)
136 		return (DLADM_STATUS_BADARG);
137 
138 	if ((flags & DLADM_OPT_TEMP) == 0)
139 		goto persist;
140 
141 	bzero(&secobj_set, sizeof (secobj_set));
142 	objp = &secobj_set.ss_obj;
143 	if (!dladm_convert_secobjclass(class, &objp->so_class))
144 		return (DLADM_STATUS_BADARG);
145 
146 	(void) strlcpy(objp->so_name, obj_name, DLD_SECOBJ_NAME_MAX);
147 	bcopy(obj_val, objp->so_val, obj_len);
148 	objp->so_len = obj_len;
149 
150 	if ((flags & DLADM_OPT_CREATE) != 0)
151 		secobj_set.ss_flags = DLD_SECOBJ_OPT_CREATE;
152 
153 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
154 		return (dladm_errno2status(errno));
155 
156 	if (i_dladm_ioctl(fd, DLDIOCSECOBJSET, &secobj_set,
157 	    sizeof (secobj_set)) < 0)
158 		status = dladm_errno2status(errno);
159 
160 	(void) close(fd);
161 	if (status != DLADM_STATUS_OK)
162 		return (status);
163 
164 persist:
165 	if ((flags & DLADM_OPT_PERSIST) != 0) {
166 		status = i_dladm_set_secobj_db(obj_name, class,
167 		    obj_val, obj_len);
168 	}
169 	return (status);
170 }
171 
172 dladm_status_t
173 dladm_get_secobj(const char *obj_name, dladm_secobj_class_t *classp,
174     uint8_t *obj_val, uint_t *obj_lenp, uint_t flags)
175 {
176 	int			fd;
177 	dladm_status_t		status = DLADM_STATUS_OK;
178 	dld_ioc_secobj_get_t	secobj_get;
179 	dld_secobj_t		*objp;
180 
181 	if (obj_name == NULL || strlen(obj_name) > DLD_SECOBJ_NAME_MAX ||
182 	    obj_val == NULL || obj_lenp == NULL || *obj_lenp == 0 ||
183 	    *obj_lenp > DLD_SECOBJ_VAL_MAX)
184 		return (DLADM_STATUS_BADARG);
185 
186 	if ((flags & DLADM_OPT_PERSIST) != 0) {
187 		return (i_dladm_get_secobj_db(obj_name, classp,
188 		    obj_val, obj_lenp));
189 	}
190 
191 	bzero(&secobj_get, sizeof (secobj_get));
192 	objp = &secobj_get.sg_obj;
193 	(void) strlcpy(objp->so_name, obj_name, DLD_SECOBJ_NAME_MAX);
194 
195 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
196 		return (dladm_errno2status(errno));
197 
198 	if (i_dladm_ioctl(fd, DLDIOCSECOBJGET, &secobj_get,
199 	    sizeof (secobj_get)) < 0)
200 		status = dladm_errno2status(errno);
201 
202 	(void) close(fd);
203 	if (objp->so_len > *obj_lenp)
204 		return (DLADM_STATUS_TOOSMALL);
205 
206 	if (!dladm_convert_dldsecobjclass(objp->so_class, classp))
207 		return (DLADM_STATUS_FAILED);
208 
209 	*obj_lenp = objp->so_len;
210 	bcopy(objp->so_val, obj_val, *obj_lenp);
211 	return (status);
212 }
213 
214 dladm_status_t
215 dladm_unset_secobj(const char *obj_name, uint_t flags)
216 {
217 	int			fd;
218 	dladm_status_t		status = DLADM_STATUS_OK;
219 	dld_ioc_secobj_unset_t	secobj_unset;
220 
221 	if (obj_name == NULL || strlen(obj_name) > DLD_SECOBJ_NAME_MAX ||
222 	    flags == 0)
223 		return (DLADM_STATUS_BADARG);
224 
225 	if ((flags & DLADM_OPT_TEMP) == 0)
226 		goto persist;
227 
228 	bzero(&secobj_unset, sizeof (secobj_unset));
229 	(void) strlcpy(secobj_unset.su_name, obj_name, DLD_SECOBJ_NAME_MAX);
230 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
231 		return (dladm_errno2status(errno));
232 
233 	if (i_dladm_ioctl(fd, DLDIOCSECOBJUNSET, &secobj_unset,
234 	    sizeof (secobj_unset)) < 0)
235 		status = dladm_errno2status(errno);
236 
237 	(void) close(fd);
238 	if (status != DLADM_STATUS_OK)
239 		return (status);
240 
241 persist:
242 	if ((flags & DLADM_OPT_PERSIST) != 0)
243 		status = i_dladm_unset_secobj_db(obj_name);
244 
245 	return (status);
246 }
247 
248 #define	SECOBJ_BUFSZ	65536
249 dladm_status_t
250 dladm_walk_secobj(void *arg, boolean_t (*func)(void *, const char *),
251     uint_t flags)
252 {
253 	int			fd = -1;
254 	dladm_status_t		status = DLADM_STATUS_OK;
255 	dld_ioc_secobj_get_t	*secobj_getp;
256 	dld_secobj_t		*objp;
257 
258 	if ((flags & DLADM_OPT_PERSIST) != 0)
259 		return (i_dladm_walk_secobj_db(arg, func));
260 
261 	secobj_getp = calloc(1, SECOBJ_BUFSZ);
262 	if (secobj_getp == NULL)
263 		return (DLADM_STATUS_NOMEM);
264 
265 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) {
266 		status = dladm_errno2status(errno);
267 		goto done;
268 	}
269 	if (i_dladm_ioctl(fd, DLDIOCSECOBJGET, secobj_getp,
270 	    SECOBJ_BUFSZ) < 0) {
271 		status = dladm_errno2status(errno);
272 		goto done;
273 	}
274 
275 	objp = (dld_secobj_t *)(secobj_getp + 1);
276 	while (secobj_getp->sg_count > 0) {
277 		if (!func(arg, objp->so_name))
278 			goto done;
279 		secobj_getp->sg_count--;
280 		objp++;
281 	}
282 done:
283 	(void) close(fd);
284 	free(secobj_getp);
285 	return (status);
286 }
287 
288 /*
289  * Data structures used for implementing persistent secure objects
290  */
291 typedef struct secobj_info {
292 	const char		*si_name;
293 	dladm_secobj_class_t	*si_classp;
294 	uint8_t			*si_val;
295 	uint_t			*si_lenp;
296 } secobj_info_t;
297 
298 typedef struct secobj_name {
299 	char			*sn_name;
300 	struct secobj_name	*sn_next;
301 } secobj_name_t;
302 
303 typedef struct secobj_db_state	secobj_db_state_t;
304 
305 typedef boolean_t (*secobj_db_op_t)(struct secobj_db_state *, char *,
306     secobj_info_t *, dladm_status_t *);
307 
308 struct secobj_db_state {
309 	secobj_db_op_t		ss_op;
310 	secobj_info_t		ss_info;
311 	secobj_name_t		**ss_namelist;
312 };
313 
314 /*
315  * Update or generate a secobj entry using the info in ssp->ss_info.
316  */
317 /* ARGSUSED */
318 static boolean_t
319 process_secobj_set(secobj_db_state_t *ssp, char *buf, secobj_info_t *sip,
320     dladm_status_t *statusp)
321 {
322 	char	tmpbuf[MAXLINELEN];
323 	char	classbuf[DLADM_STRSIZE];
324 	char	*ptr = tmpbuf, *lim = tmpbuf + MAXLINELEN;
325 	int	i;
326 
327 	sip = &ssp->ss_info;
328 
329 	ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s\t", sip->si_name);
330 	ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s\t",
331 	    dladm_secobjclass2str(*sip->si_classp, classbuf));
332 
333 	ptr += snprintf(ptr, BUFLEN(lim, ptr), "0x");
334 	for (i = 0; i < *sip->si_lenp; i++) {
335 		ptr += snprintf(ptr, BUFLEN(lim, ptr), "%02x",
336 		    sip->si_val[i] & 0xff);
337 	}
338 	if (ptr > lim) {
339 		*statusp = DLADM_STATUS_TOOSMALL;
340 		return (B_FALSE);
341 	}
342 	(void) snprintf(buf, MAXLINELEN, "%s\n", tmpbuf);
343 	return (B_FALSE);
344 }
345 
346 /* ARGSUSED */
347 static boolean_t
348 process_secobj_get(secobj_db_state_t *ssp, char *buf, secobj_info_t *sip,
349     dladm_status_t *statusp)
350 {
351 	if (*sip->si_lenp > *ssp->ss_info.si_lenp) {
352 		*statusp = DLADM_STATUS_TOOSMALL;
353 		return (B_FALSE);
354 	}
355 	bcopy(sip->si_val, ssp->ss_info.si_val, *sip->si_lenp);
356 	*ssp->ss_info.si_lenp = *sip->si_lenp;
357 	*ssp->ss_info.si_classp = *sip->si_classp;
358 	return (B_FALSE);
359 }
360 
361 /* ARGSUSED */
362 static boolean_t
363 process_secobj_unset(secobj_db_state_t *ssp, char *buf, secobj_info_t *sip,
364     dladm_status_t *statusp)
365 {
366 	/*
367 	 * Delete line.
368 	 */
369 	buf[0] = '\0';
370 	return (B_FALSE);
371 }
372 
373 /* ARGSUSED */
374 static boolean_t
375 process_secobj_walk(secobj_db_state_t *ssp, char *buf, secobj_info_t *sip,
376     dladm_status_t *statusp)
377 {
378 	secobj_name_t	*snp;
379 
380 	if ((snp = malloc(sizeof (*snp))) == NULL)
381 		return (B_TRUE);
382 
383 	if ((snp->sn_name = strdup(sip->si_name)) == NULL) {
384 		free(snp);
385 		return (B_TRUE);
386 	}
387 
388 	snp->sn_next = NULL;
389 	*ssp->ss_namelist = snp;
390 	ssp->ss_namelist = &snp->sn_next;
391 	return (B_TRUE);
392 }
393 
394 /* ARGSUSED */
395 static boolean_t
396 process_secobj_init(secobj_db_state_t *ssp, char *buf, secobj_info_t *sip,
397     dladm_status_t *statusp)
398 {
399 	*statusp = dladm_set_secobj(sip->si_name, *sip->si_classp, sip->si_val,
400 	    *sip->si_lenp, DLADM_OPT_TEMP | DLADM_OPT_CREATE);
401 	return (B_TRUE);
402 }
403 
404 static int
405 parse_secobj_val(char *buf, secobj_info_t *sip)
406 {
407 	if (strncmp(buf, "0x", 2) != 0)
408 		return (EINVAL);
409 
410 	return (hexascii_to_octet(buf + 2, strlen(buf) - 2,
411 	    sip->si_val, sip->si_lenp));
412 }
413 
414 static boolean_t
415 process_secobj_line(secobj_db_state_t *ssp, char *buf,
416     dladm_status_t *statusp)
417 {
418 	secobj_info_t		sinfo;
419 	dladm_secobj_class_t	class;
420 	uint8_t			val[DLADM_SECOBJ_VAL_MAX];
421 	uint_t			vlen;
422 	int			i, len, nlen;
423 	char			*str, *lasts;
424 
425 	/*
426 	 * Skip leading spaces, blank lines, and comments.
427 	 */
428 	len = strlen(buf);
429 	for (i = 0; i < len; i++) {
430 		if (!isspace(buf[i]))
431 			break;
432 	}
433 	if (i == len || buf[i] == '#')
434 		return (B_TRUE);
435 
436 	str = buf + i;
437 	if (ssp->ss_info.si_name != NULL) {
438 		/*
439 		 * Skip objects we're not interested in.
440 		 */
441 		nlen = strlen(ssp->ss_info.si_name);
442 		if (strncmp(str, ssp->ss_info.si_name, nlen) != 0 ||
443 		    !isspace(str[nlen]))
444 			return (B_TRUE);
445 
446 		sinfo.si_name = ssp->ss_info.si_name;
447 	} else {
448 		/*
449 		 * If an object is not specified, find the object name
450 		 * and assign it to sinfo.si_name.
451 		 */
452 		if (strtok_r(str, " \n\t", &lasts) == NULL)
453 			goto fail;
454 
455 		nlen = strlen(str);
456 		sinfo.si_name = str;
457 	}
458 	str += nlen + 1;
459 	if (str >= buf + len)
460 		goto fail;
461 
462 	/*
463 	 * Find the class name.
464 	 */
465 	if ((str = strtok_r(str, " \n\t", &lasts)) == NULL)
466 		goto fail;
467 
468 	*statusp = dladm_str2secobjclass(str, &class);
469 	if (*statusp != DLADM_STATUS_OK)
470 		goto fail;
471 
472 	/*
473 	 * Find the object value.
474 	 */
475 	if ((str = strtok_r(NULL, " \n\t", &lasts)) == NULL)
476 		goto fail;
477 
478 	vlen = DLADM_SECOBJ_VAL_MAX;
479 	sinfo.si_classp = &class;
480 	sinfo.si_val = val;
481 	sinfo.si_lenp = &vlen;
482 	if (parse_secobj_val(str, &sinfo) != 0)
483 		goto fail;
484 
485 	return ((*ssp->ss_op)(ssp, buf, &sinfo, statusp));
486 
487 fail:
488 	/*
489 	 * Delete corrupted line.
490 	 */
491 	buf[0] = '\0';
492 	return (B_TRUE);
493 }
494 
495 static dladm_status_t
496 process_secobj_db(void *arg, FILE *fp, FILE *nfp)
497 {
498 	secobj_db_state_t	*ssp = arg;
499 	dladm_status_t		status = DLADM_STATUS_OK;
500 	char			buf[MAXLINELEN];
501 	boolean_t		cont = B_TRUE;
502 
503 	/*
504 	 * This loop processes each line of the configuration file.
505 	 * buf can potentially be modified by process_secobj_line().
506 	 * If this is a write operation and buf is not truncated, buf will
507 	 * be written to disk. process_secobj_line() will no longer be
508 	 * called after it returns B_FALSE; at which point the remainder
509 	 * of the file will continue to be read and, if necessary, written
510 	 * to disk as well.
511 	 */
512 	while (fgets(buf, MAXLINELEN, fp) != NULL) {
513 		if (cont)
514 			cont = process_secobj_line(ssp, buf, &status);
515 
516 		if (nfp != NULL && buf[0] != '\0' && fputs(buf, nfp) == EOF) {
517 			status = dladm_errno2status(errno);
518 			break;
519 		}
520 	}
521 	if (status != DLADM_STATUS_OK || !cont)
522 		return (status);
523 
524 	if (ssp->ss_op == process_secobj_set) {
525 		/*
526 		 * If the specified object is not found above, we add the
527 		 * object to the configuration file.
528 		 */
529 		(void) (*ssp->ss_op)(ssp, buf, NULL, &status);
530 		if (status == DLADM_STATUS_OK && fputs(buf, nfp) == EOF)
531 			status = dladm_errno2status(errno);
532 	}
533 
534 	if (ssp->ss_op == process_secobj_unset ||
535 	    ssp->ss_op == process_secobj_get)
536 		status = DLADM_STATUS_NOTFOUND;
537 
538 	return (status);
539 }
540 
541 #define	SECOBJ_RW_DB(statep, writeop) \
542 	(i_dladm_rw_db("/etc/dladm/secobj.conf", S_IRUSR | S_IWUSR, \
543 	process_secobj_db, (statep), (writeop)))
544 
545 static dladm_status_t
546 i_dladm_set_secobj_db(const char *obj_name, dladm_secobj_class_t class,
547     uint8_t *obj_val, uint_t obj_len)
548 {
549 	secobj_db_state_t	state;
550 
551 	state.ss_op = process_secobj_set;
552 	state.ss_info.si_name = obj_name;
553 	state.ss_info.si_classp = &class;
554 	state.ss_info.si_val = obj_val;
555 	state.ss_info.si_lenp = &obj_len;
556 	state.ss_namelist = NULL;
557 
558 	return (SECOBJ_RW_DB(&state, B_TRUE));
559 }
560 
561 static dladm_status_t
562 i_dladm_get_secobj_db(const char *obj_name, dladm_secobj_class_t *classp,
563     uint8_t *obj_val, uint_t *obj_lenp)
564 {
565 	secobj_db_state_t	state;
566 
567 	state.ss_op = process_secobj_get;
568 	state.ss_info.si_name = obj_name;
569 	state.ss_info.si_classp = classp;
570 	state.ss_info.si_val = obj_val;
571 	state.ss_info.si_lenp = obj_lenp;
572 	state.ss_namelist = NULL;
573 
574 	return (SECOBJ_RW_DB(&state, B_FALSE));
575 }
576 
577 static dladm_status_t
578 i_dladm_unset_secobj_db(const char *obj_name)
579 {
580 	secobj_db_state_t	state;
581 
582 	state.ss_op = process_secobj_unset;
583 	state.ss_info.si_name = obj_name;
584 	state.ss_info.si_classp = NULL;
585 	state.ss_info.si_val = NULL;
586 	state.ss_info.si_lenp = NULL;
587 	state.ss_namelist = NULL;
588 
589 	return (SECOBJ_RW_DB(&state, B_TRUE));
590 }
591 
592 static dladm_status_t
593 i_dladm_walk_secobj_db(void *arg, boolean_t (*func)(void *, const char *))
594 {
595 	secobj_db_state_t	state;
596 	secobj_name_t		*snp = NULL, *fsnp;
597 	dladm_status_t		status;
598 	boolean_t		cont = B_TRUE;
599 
600 	state.ss_op = process_secobj_walk;
601 	state.ss_info.si_name = NULL;
602 	state.ss_info.si_classp = NULL;
603 	state.ss_info.si_val = NULL;
604 	state.ss_info.si_lenp = NULL;
605 	state.ss_namelist = &snp;
606 
607 	status = SECOBJ_RW_DB(&state, B_FALSE);
608 	if (status != DLADM_STATUS_OK)
609 		return (status);
610 
611 	while (snp != NULL) {
612 		fsnp = snp;
613 		snp = snp->sn_next;
614 		if (cont)
615 			cont = func(arg, fsnp->sn_name);
616 		free(fsnp->sn_name);
617 		free(fsnp);
618 	}
619 	return (status);
620 }
621 
622 dladm_status_t
623 dladm_init_secobj(void)
624 {
625 	secobj_db_state_t	state;
626 
627 	state.ss_op = process_secobj_init;
628 	state.ss_info.si_name = NULL;
629 	state.ss_info.si_classp = NULL;
630 	state.ss_info.si_val = NULL;
631 	state.ss_info.si_lenp = NULL;
632 	state.ss_namelist = NULL;
633 
634 	return (SECOBJ_RW_DB(&state, B_FALSE));
635 }
636