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 2010 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 *
25 * From "misc.c	5.15	00/05/31 SMI; TSOL 2.x"
26 */
27
28/*
29 *	Miscellaneous user interfaces to trusted label functions.
30 */
31
32
33#include <ctype.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <strings.h>
37#include <errno.h>
38#include <libintl.h>
39#include <libtsnet.h>
40#include <tsol/label.h>
41
42#include <net/route.h>
43
44#define	MAX_ATTR_LEN	1024
45
46/*
47 * Parse off an entry from a line.  Entry is stored in 'outbuf'.  Returned
48 * value is a pointer to the first unprocessed input character from 'instr'.
49 */
50const char *
51parse_entry(char *outbuf, size_t outlen, const char *instr,
52    const char *delimit)
53{
54	boolean_t escape_state = B_FALSE;
55	boolean_t any_white;
56	char chr;
57
58	any_white = strchr(delimit, '\n') != NULL;
59
60	/*
61	 * User may specify outlen as 0 to skip over a field without storing
62	 * it anywhere.  Otherwise, we need at least one byte for the
63	 * terminating NUL plus one byte to store another byte from instr.
64	 */
65	while (outlen != 1 && (chr = *instr++) != '\0') {
66		if (!escape_state) {
67			if (chr == '\\') {
68				escape_state = B_TRUE;
69				continue;
70			}
71			if (strchr(delimit, chr) != NULL)
72				break;
73			if (any_white && isspace(chr))
74				break;
75		}
76		escape_state = B_FALSE;
77		if (outlen > 0) {
78			*outbuf++ = chr;
79			outlen--;
80		}
81	}
82	if (outlen != 1)
83		instr--;
84	if (escape_state)
85		instr--;
86	if (outlen > 0)
87		*outbuf = '\0';
88	return (instr);
89}
90
91char *
92sl_to_str(const m_label_t *sl)
93{
94	char *sl_str = NULL;
95	static char unknown_str[] = "UNKNOWN";
96
97	if (sl == NULL)
98		return (strdup(unknown_str));
99
100	if ((label_to_str(sl, &sl_str, M_LABEL, DEF_NAMES) != 0) &&
101	    (label_to_str(sl, &sl_str, M_INTERNAL, DEF_NAMES) != 0))
102		return (strdup(unknown_str));
103
104	return (sl_str);
105}
106
107static const char *rtsa_keywords[] = {
108#define	SAK_MINSL	0
109	"min_sl",
110#define	SAK_MAXSL	1
111	"max_sl",
112#define	SAK_DOI		2
113	"doi",
114#define	SAK_CIPSO	3
115	"cipso",
116#define	SAK_SL		4
117	"sl",
118#define	SAK_INVAL	5
119	NULL
120};
121
122const char *
123rtsa_to_str(const struct rtsa_s *rtsa, char *line, size_t len)
124{
125	size_t slen;
126	uint32_t mask, i;
127	char *sl_str = NULL;
128
129	slen = 0;
130	*line = '\0';
131	mask = rtsa->rtsa_mask;
132
133	for (i = 1; mask != 0 && i != 0 && slen < len - 1; i <<= 1) {
134		if (!(i & (RTSA_MINSL|RTSA_MAXSL|RTSA_DOI|RTSA_CIPSO)))
135			continue;
136		if (!(i & mask))
137			continue;
138		if (slen != 0)
139			line[slen++] = ',';
140		switch (i & mask) {
141		case RTSA_MINSL:
142			if ((mask & RTSA_MAXSL) &&
143			    blequal(&rtsa->rtsa_slrange.lower_bound,
144			    &rtsa->rtsa_slrange.upper_bound)) {
145
146				sl_str =
147				    sl_to_str(&rtsa->rtsa_slrange.lower_bound);
148				slen += snprintf(line + slen, len - slen,
149				    "sl=%s", sl_str);
150				free(sl_str);
151				sl_str = NULL;
152				mask ^= RTSA_MAXSL;
153				break;
154			}
155			sl_str = sl_to_str(&rtsa->rtsa_slrange.lower_bound);
156			slen += snprintf(line + slen, len - slen, "min_sl=%s",
157			    sl_str);
158			free(sl_str);
159			sl_str = NULL;
160			break;
161		case RTSA_MAXSL:
162			sl_str = sl_to_str(&rtsa->rtsa_slrange.upper_bound);
163			slen += snprintf(line + slen, len - slen, "max_sl=%s",
164			    sl_str);
165			free(sl_str);
166			sl_str = NULL;
167			break;
168		case RTSA_DOI:
169			slen += snprintf(line + slen, len - slen, "doi=%d",
170			    rtsa->rtsa_doi);
171			break;
172		case RTSA_CIPSO:
173			slen += snprintf(line + slen, len - slen, "cipso");
174			break;
175		}
176	}
177
178	return (line);
179}
180
181boolean_t
182rtsa_keyword(const char *options, struct rtsa_s *sp, int *errp, char **errstrp)
183{
184	const char *valptr, *nxtopt;
185	uint32_t mask = 0, doi;
186	int key;
187	m_label_t *min_sl = NULL, *max_sl = NULL;
188	char attrbuf[MAX_ATTR_LEN];
189	const char **keyword;
190	int err;
191	char *errstr, *cp;
192
193	if (errp == NULL)
194		errp = &err;
195	if (errstrp == NULL)
196		errstrp = &errstr;
197
198	*errstrp = (char *)options;
199
200	while (*options != '\0') {
201		valptr = parse_entry(attrbuf, sizeof (attrbuf), options, ",=");
202
203		if (attrbuf[0] == '\0') {
204			*errstrp = (char *)options;
205			*errp = LTSNET_ILL_ENTRY;
206			goto out_err;
207		}
208		for (keyword = rtsa_keywords; *keyword != NULL; keyword++)
209			if (strcmp(*keyword, attrbuf) == 0)
210				break;
211		if ((key = keyword - rtsa_keywords) == SAK_INVAL) {
212			*errstrp = (char *)options;
213			*errp = LTSNET_ILL_KEY;
214			goto out_err;
215		}
216		if ((key == SAK_CIPSO && *valptr == '=') ||
217		    (key != SAK_CIPSO && *valptr != '=')) {
218			*errstrp = (char *)valptr;
219			*errp = LTSNET_ILL_VALDELIM;
220			goto out_err;
221		}
222
223		nxtopt = valptr;
224		if (*valptr == '=') {
225			valptr++;
226			nxtopt = parse_entry(attrbuf, sizeof (attrbuf),
227			    valptr, ",=");
228			if (*nxtopt == '=') {
229				*errstrp = (char *)nxtopt;
230				*errp = LTSNET_ILL_KEYDELIM;
231				goto out_err;
232			}
233		}
234		if (*nxtopt == ',')
235			nxtopt++;
236
237		switch (key) {
238		case SAK_MINSL:
239			if (mask & RTSA_MINSL) {
240				*errstrp = (char *)options;
241				*errp = LTSNET_DUP_KEY;
242				goto out_err;
243			}
244			m_label_free(min_sl);		/* in case of duplicate */
245			min_sl = NULL;
246			if (str_to_label(attrbuf, &min_sl, MAC_LABEL,
247			    L_NO_CORRECTION, NULL) != 0) {
248				*errstrp = (char *)valptr;
249				*errp = LTSNET_ILL_LOWERBOUND;
250				goto out_err;
251			}
252			mask |= RTSA_MINSL;
253			break;
254
255		case SAK_MAXSL:
256			if (mask & RTSA_MAXSL) {
257				*errstrp = (char *)options;
258				*errp = LTSNET_DUP_KEY;
259				goto out_err;
260			}
261			m_label_free(max_sl);		/* in case of duplicate */
262			max_sl = NULL;
263			if (str_to_label(attrbuf, &max_sl, MAC_LABEL,
264			    L_NO_CORRECTION, NULL) != 0) {
265				*errstrp = (char *)valptr;
266				*errp = LTSNET_ILL_UPPERBOUND;
267				goto out_err;
268			}
269			mask |= RTSA_MAXSL;
270			break;
271
272		case SAK_SL:
273			if (mask & (RTSA_MAXSL|RTSA_MINSL)) {
274				*errstrp = (char *)options;
275				*errp = LTSNET_DUP_KEY;
276				goto out_err;
277			}
278			m_label_free(min_sl);		/* in case of duplicate */
279			min_sl = NULL;
280			if (str_to_label(attrbuf, &min_sl, MAC_LABEL,
281			    L_NO_CORRECTION, NULL) != 0) {
282				*errstrp = (char *)valptr;
283				*errp = LTSNET_ILL_LABEL;
284				goto out_err;
285			}
286			*max_sl = *min_sl;
287			mask |= (RTSA_MINSL | RTSA_MAXSL);
288			break;
289
290		case SAK_DOI:
291			if (mask & RTSA_DOI) {
292				*errstrp = (char *)options;
293				*errp = LTSNET_DUP_KEY;
294				goto out_err;
295			}
296			errno = 0;
297			doi = strtoul(attrbuf, &cp, 0);
298			if (doi == 0 || errno != 0 || *cp != '\0') {
299				*errstrp = (char *)valptr;
300				*errp = LTSNET_ILL_DOI;
301				goto out_err;
302			}
303			mask |= RTSA_DOI;
304			break;
305
306		case SAK_CIPSO:
307			if (mask & RTSA_CIPSO) {
308				*errstrp = (char *)options;
309				*errp = LTSNET_DUP_KEY;
310				goto out_err;
311			}
312			mask |= RTSA_CIPSO;
313			break;
314		}
315
316		options = nxtopt;
317	}
318
319	/* Defaults to CIPSO if not specified */
320	mask |= RTSA_CIPSO;
321
322	/* If RTSA_CIPSO is specified, RTSA_DOI must be specified */
323	if (!(mask & RTSA_DOI)) {
324		*errp = LTSNET_NO_DOI;
325		goto out_err;
326	}
327
328	/* SL range must be specified */
329	if (!(mask & (RTSA_MINSL|RTSA_MAXSL))) {
330		*errp = LTSNET_NO_RANGE;
331		goto out_err;
332	}
333	if (!(mask & RTSA_MINSL)) {
334		*errp = LTSNET_NO_LOWERBOUND;
335		goto out_err;
336	}
337	if (!(mask & RTSA_MAXSL)) {
338		*errp = LTSNET_NO_UPPERBOUND;
339		goto out_err;
340	}
341
342	/* SL range must have upper bound dominating lower bound */
343	if (!bldominates(max_sl, min_sl)) {
344		*errp = LTSNET_ILL_RANGE;
345		goto out_err;
346	}
347
348	if (mask & RTSA_MINSL)
349		sp->rtsa_slrange.lower_bound = *min_sl;
350	if (mask & RTSA_MAXSL)
351		sp->rtsa_slrange.upper_bound = *max_sl;
352	if (mask & RTSA_DOI)
353		sp->rtsa_doi = doi;
354	sp->rtsa_mask = mask;
355
356	m_label_free(min_sl);
357	m_label_free(max_sl);
358
359	return (B_TRUE);
360
361out_err:
362	m_label_free(min_sl);
363	m_label_free(max_sl);
364
365	return (B_FALSE);
366}
367
368/* Keep in sync with libtsnet.h */
369static const char *tsol_errlist[] = {
370	"No error",
371	"System error",
372	"Empty string or end of list",
373	"Entry is malformed",
374	"Missing name",
375	"Missing attributes",
376	"Illegal name",
377	"Illegal keyword delimiter",
378	"Unknown keyword",
379	"Duplicate keyword",
380	"Illegal value delimiter",
381	"Missing host type",
382	"Illegal host type",
383	"Missing label",
384	"Illegal label",
385	"Missing label range",
386	"Illegal label range",
387	"No lower bound in range",
388	"Illegal lower bound in range",
389	"No upper bound in range",
390	"Illegal upper bound in range",
391	"Missing DOI",
392	"Illegal DOI",
393	"Too many entries in set",
394	"Missing address/network",
395	"Illegal address/network",
396	"Illegal flag",
397	"Illegal MLP specification",
398	"Unacceptable keyword for type"
399};
400static const int tsol_nerr = sizeof (tsol_errlist) / sizeof (*tsol_errlist);
401
402const char *
403tsol_strerror(int libtserr, int errnoval)
404{
405	if (libtserr == LTSNET_SYSERR)
406		return (strerror(errnoval));
407	if (libtserr >= 0 && libtserr < tsol_nerr)
408		return (gettext(tsol_errlist[libtserr]));
409	return (gettext("Unknown error"));
410}
411