xref: /illumos-gate/usr/src/cmd/devfsadm/devpolicy.c (revision 88f8b78a88cbdc6d8c1af5c3e54bc49d25095c98)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <ctype.h>
33 #include <priv.h>
34 #include <string.h>
35 #include <libgen.h>
36 #include <errno.h>
37 #include <libintl.h>
38 #include <sys/devpolicy.h>
39 #include <sys/modctl.h>
40 #include "message.h"
41 #include "plcysubr.h"
42 
43 /* Cannot include devfsadm_impl.h because of static definitions */
44 #define	err_print	devfsadm_errprint
45 extern void err_print(char *, ...);
46 
47 #define	PLCY_CHUNK	128
48 
49 /*
50  * devpolicy sort order sorts on three items to help the kernel;
51  * the kernel will verify but not sort.
52  *
53  *	1) major number - but default major will be first in sorted output
54  *	2) wildcard or not - non wildcard entries are sorted first.
55  *		2a) Expanded minor numbers first (empty name sorts first).
56  *		2b) Named minors.
57  *	3) length of wildcard entry - longest pattern first
58  *
59  * The last rule allows patterns such as *ctl and * to be used both
60  * unambiguously instead of current bogosities as found in /etc/minor_perm:
61  *	rtvc:ctl 0644 root sys
62  *	rtvc:rtvcctl* 0644 root sys
63  *	rtvc:rtvc[!ctl]* 0666 root sys
64  *
65  * The last pattern only works by accident.
66  *
67  * This would simply become (in sorted order):
68  *	rtvc:ctl
69  *	rtvc:rtvcctl*
70  *	rtvc:*
71  */
72 
73 static int
74 qcmp(const void *a, const void *b)
75 {
76 	const devplcysys_t *pa = a;
77 	const devplcysys_t *pb = b;
78 	int wilda, wildb;
79 
80 	/* sort on major number, default major first in sort output */
81 	if (pa->dps_maj == DEVPOLICY_DFLT_MAJ)
82 		return (-1);
83 	if (pb->dps_maj == DEVPOLICY_DFLT_MAJ)
84 		return (1);
85 
86 	if (pa->dps_maj > pb->dps_maj)
87 		return (1);
88 	else if (pa->dps_maj < pb->dps_maj)
89 		return (-1);
90 
91 	wilda = strchr(pa->dps_minornm, '*') != NULL;
92 	wildb = strchr(pb->dps_minornm, '*') != NULL;
93 
94 	/* sort the entry with the wildcard last */
95 	if (wilda != wildb)
96 		return (wilda - wildb);
97 
98 	/* entries without wildcards compare with strcmp() */
99 	if (wilda == 0)
100 		return (strcmp(pa->dps_minornm, pb->dps_minornm));
101 
102 	/* shortest wildcard last */
103 	return ((int)(strlen(pb->dps_minornm) - strlen(pa->dps_minornm)));
104 }
105 
106 static int
107 loadprivs(const char *infile)
108 {
109 	char *line, *col;
110 	FILE *in;
111 	struct fileentry *fep;
112 	int res = 0;
113 
114 	in = fopen(infile, "r");
115 
116 	if (in == NULL)
117 		return (0);
118 
119 	while ((fep = fgetline(in)) != NULL && fep->entry != NULL) {
120 		line = fep->entry;
121 
122 		if (*line == '\0')
123 			continue;
124 
125 		line[strlen(line)-1] = '\0';
126 
127 		col = strchr(line, ':');
128 
129 		if (col != NULL) {
130 			major_t maj;
131 			*col = '\0';
132 
133 			if (modctl(MODGETMAJBIND, line, col - line + 1, &maj)
134 			    != 0)
135 				continue;
136 
137 			line = col + 1;
138 		}
139 
140 		if (modctl(MODALLOCPRIV, line) != 0) {
141 			(void) err_print("modctl(MODALLOCPRIV, %s): %s\n",
142 				line, strerror(errno));
143 			res = -1;
144 		}
145 	}
146 	return (res);
147 }
148 
149 static int
150 loadpolicy(const char *infile)
151 {
152 	char *line;
153 	int nalloc = 0, cnt = 0;
154 	char *mem = NULL;
155 	devplcysys_t *dp, *dflt = NULL;
156 	FILE *in;
157 	struct fileentry *fep;
158 	int res;
159 
160 	char *maj;
161 	char *tok;
162 	char *min;
163 
164 	in = fopen(infile, "r");
165 
166 	if (in == NULL) {
167 		err_print(OPEN_FAILED, infile, strerror(errno));
168 		return (-1);
169 	}
170 
171 	while ((fep = fgetline(in)) != NULL && fep->entry != NULL) {
172 		line = fep->entry;
173 		if (cnt >= nalloc) {
174 			nalloc += PLCY_CHUNK;
175 			mem = realloc(mem, nalloc * devplcysys_sz);
176 			if (mem == NULL) {
177 				err_print(MALLOC_FAILED,
178 					nalloc * devplcysys_sz);
179 				return (-1);
180 			}
181 
182 			/* Readjust pointer to dflt after realloc */
183 			if (dflt != NULL)
184 				/* LINTED: alignment */
185 				dflt = (devplcysys_t *)mem;
186 		}
187 		maj = strtok(line, "\n\t ");
188 
189 		if (maj == NULL)
190 			continue;
191 
192 		/* LINTED: alignment */
193 		dp = (devplcysys_t *)(mem + devplcysys_sz * cnt);
194 
195 		if (strcmp(maj, "*") == 0) {
196 			if (dflt != NULL) {
197 				err_print(DPLCY_ONE_DFLT, infile);
198 				return (-1);
199 			}
200 			(void) memset(dp, 0, devplcysys_sz);
201 			dp->dps_maj = DEVPOLICY_DFLT_MAJ;
202 			dflt = dp;
203 		} else {
204 			if (dflt == NULL) {
205 				err_print(DPLCY_FIRST, infile);
206 				return (-1);
207 			}
208 
209 			(void) memcpy(dp, dflt, devplcysys_sz);
210 
211 			min = strchr(maj, ':');
212 
213 			if (min != NULL) {
214 				*min++ = '\0';
215 				if (strchr(min, ':') != NULL) {
216 					(void) fprintf(stderr,
217 					    "Too many ``:'' in entry\n");
218 					return (-1);
219 				}
220 			} else
221 				min = "*";
222 
223 			/* Silently ignore unknown devices. */
224 			if (modctl(MODGETMAJBIND, maj, strlen(maj) + 1,
225 			    &dp->dps_maj) != 0)
226 				continue;
227 
228 			if (*min == '(') {
229 				/* Numeric minor range */
230 				char type;
231 
232 				if (parse_minor_range(min, &dp->dps_lomin,
233 				    &dp->dps_himin, &type) == -1) {
234 					err_print(INVALID_MINOR, min);
235 					return (-1);
236 				}
237 				dp->dps_isblock = type == 'b';
238 			} else {
239 				if (strlen(min) >= sizeof (dp->dps_minornm)) {
240 					err_print(MINOR_TOO_LONG, maj, min);
241 					return (-1);
242 				}
243 				(void) strcpy(dp->dps_minornm, min);
244 			}
245 		}
246 
247 		while (tok = strtok(NULL, "\n\t ")) {
248 			if (parse_plcy_token(tok, dp)) {
249 				err_print(BAD_ENTRY, fep->startline,
250 					fep->orgentry);
251 				return (-1);
252 			}
253 		}
254 		cnt++;
255 	}
256 	if (fep == NULL) {
257 		if (feof(in))
258 			err_print(UNEXPECTED_EOF, infile);
259 		else
260 			err_print(NO_MEMORY);
261 		return (-1);
262 	}
263 	qsort(mem, cnt, devplcysys_sz, qcmp);
264 
265 	if ((res = modctl(MODSETDEVPOLICY, cnt, devplcysys_sz, mem)) != 0)
266 		err_print("modctl(MODSETDEVPOLICY): %s\n", strerror(errno));
267 
268 	return (res);
269 }
270 
271 int
272 load_devpolicy(void)
273 {
274 	int res;
275 
276 	devplcy_init();
277 
278 	res = loadprivs(EXTRA_PRIVS);
279 	res += loadpolicy(DEV_POLICY);
280 
281 	return (res);
282 }
283