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