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