1 /* $NetBSD: verified_exec.c,v 1.5.2.16 2005/08/16 12:43:17 tron Exp $ */
2
3 /*-
4 * Copyright 2005 Elad Efrat <elad@bsd.org.il>
5 * Copyright 2005 Brett Lymn <blymn@netbsd.org>
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Brett Lymn and Elad Efrat
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Neither the name of The NetBSD Foundation nor the names of its
16 * contributors may be used to endorse or promote products derived
17 * from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 #if defined(__NetBSD__)
34 __KERNEL_RCSID(0, "$NetBSD: verified_exec.c,v 1.5.2.16 2005/08/16 12:43:17 tron Exp $");
35 #else
36 __RCSID("$Id: verified_exec.c,v 1.5.2.16 2005/08/16 12:43:17 tron Exp $\n$NetBSD: verified_exec.c,v 1.5.2.16 2005/08/16 12:43:17 tron Exp $");
37 #endif
38
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/proc.h>
42 #include <sys/errno.h>
43 #include <sys/buf.h>
44 #include <sys/malloc.h>
45
46 #ifdef __FreeBSD__
47 #include <sys/kernel.h>
48 #include <sys/device_port.h>
49 #include <sys/ioccom.h>
50 #else
51 #include <sys/ioctl.h>
52 #include <sys/device.h>
53 #define DEVPORT_DEVICE struct device
54 #endif
55
56 #include <sys/conf.h>
57 #include <sys/lock.h>
58 #include <sys/queue.h>
59 #include <sys/vnode.h>
60 #include <sys/fcntl.h>
61 #include <sys/namei.h>
62 #include <sys/sysctl.h>
63 #define VERIEXEC_NEED_NODE
64 #include <sys/verified_exec.h>
65
66 /* count of number of times device is open (we really only allow one open) */
67 static unsigned int veriexec_dev_usage;
68
69 struct veriexec_softc {
70 DEVPORT_DEVICE veriexec_dev;
71 };
72
73 #if defined(__FreeBSD__)
74 # define CDEV_MAJOR 216
75 # define BDEV_MAJOR -1
76 #endif
77
78 const struct cdevsw veriexec_cdevsw = {
79 veriexecopen,
80 veriexecclose,
81 noread,
82 nowrite,
83 veriexecioctl,
84 #ifdef __NetBSD__
85 nostop,
86 notty,
87 #endif
88 nopoll,
89 nommap,
90 #if defined(__NetBSD__)
91 nokqfilter,
92 #elif defined(__FreeBSD__)
93 nostrategy,
94 "veriexec",
95 CDEV_MAJOR,
96 nodump,
97 nopsize,
98 0, /* flags */
99 BDEV_MAJOR
100 #endif
101 };
102
103 /* Autoconfiguration glue */
104 void veriexecattach(DEVPORT_DEVICE *parent, DEVPORT_DEVICE *self,
105 void *aux);
106 int veriexecopen(dev_t dev, int flags, int fmt, struct proc *p);
107 int veriexecclose(dev_t dev, int flags, int fmt, struct proc *p);
108 int veriexecioctl(dev_t dev, u_long cmd, caddr_t data, int flags,
109 struct proc *p);
110
111 void
112 veriexecattach(DEVPORT_DEVICE *parent, DEVPORT_DEVICE *self,
113 void *aux)
114 {
115 veriexec_dev_usage = 0;
116
117 if (veriexec_verbose >= 2)
118 printf("Veriexec: veriexecattach: Veriexec pseudo-device"
119 "attached.\n");
120 }
121
122 int
123 veriexecopen(dev_t dev __unused, int flags __unused,
124 int fmt __unused, struct proc *p __unused)
125 {
126 if (veriexec_verbose >= 2) {
127 printf("Veriexec: veriexecopen: Veriexec load device "
128 "open attempt by uid=%u, pid=%u. (dev=%u)\n",
129 p->p_ucred->cr_uid, p->p_pid, dev);
130 }
131
132 if (suser(p->p_ucred, &p->p_acflag) != 0)
133 return (EPERM);
134
135 if (veriexec_dev_usage > 0) {
136 if (veriexec_verbose >= 2)
137 printf("Veriexec: load device already in use.\n");
138
139 return(EBUSY);
140 }
141
142 veriexec_dev_usage++;
143 return (0);
144 }
145
146 int
147 veriexecclose(dev_t dev __unused, int flags __unused,
148 int fmt __unused, struct proc *p __unused)
149 {
150 if (veriexec_dev_usage > 0)
151 veriexec_dev_usage--;
152 return (0);
153 }
154
155 int
156 veriexecioctl(dev_t dev __unused, u_long cmd, caddr_t data,
157 int flags __unused, struct proc *p)
158 {
159 struct veriexec_hashtbl *tbl;
160 struct nameidata nid;
161 struct vattr va;
162 int error = 0;
163 u_long hashmask;
164
165 if (veriexec_strict > 0) {
166 printf("Veriexec: veriexecioctl: Strict mode, modifying "
167 "veriexec tables is not permitted.\n");
168
169 return (EPERM);
170 }
171
172 switch (cmd) {
173 case VERIEXEC_TABLESIZE: {
174 struct veriexec_sizing_params *params =
175 (struct veriexec_sizing_params *) data;
176 u_char node_name[16];
177
178 /* Check for existing table for device. */
179 if (veriexec_tblfind(params->dev) != NULL)
180 return (EEXIST);
181
182 /* Allocate and initialize a Veriexec hash table. */
183 tbl = malloc(sizeof(struct veriexec_hashtbl), M_TEMP,
184 M_WAITOK);
185 tbl->hash_size = params->hash_size;
186 tbl->hash_dev = params->dev;
187 tbl->hash_tbl = hashinit(params->hash_size, HASH_LIST, M_TEMP,
188 M_WAITOK, &hashmask);
189 tbl->hash_count = 0;
190
191 LIST_INSERT_HEAD(&veriexec_tables, tbl, hash_list);
192
193 snprintf(node_name, sizeof(node_name), "dev_%u",
194 tbl->hash_dev);
195
196 sysctl_createv(NULL, 0, &veriexec_count_node, NULL,
197 CTLFLAG_READONLY, CTLTYPE_QUAD, node_name,
198 NULL, NULL, 0, &tbl->hash_count, 0,
199 tbl->hash_dev, CTL_EOL);
200
201 break;
202 }
203
204 case VERIEXEC_LOAD: {
205 struct veriexec_params *params =
206 (struct veriexec_params *) data;
207 struct veriexec_hash_entry *hh;
208 struct veriexec_hash_entry *e;
209
210 NDINIT(&nid, LOOKUP, FOLLOW, UIO_SYSSPACE, params->file, p);
211 error = namei(&nid);
212 if (error)
213 return (error);
214
215 /* Add only regular files. */
216 if (nid.ni_vp->v_type != VREG) {
217 printf("Veriexec: veriexecioctl: Not adding \"%s\": "
218 "Not a regular file.\n", params->file);
219 vrele(nid.ni_vp);
220 return (EINVAL);
221 }
222
223 /* Get attributes for device and inode. */
224 error = VOP_GETATTR(nid.ni_vp, &va, p->p_ucred, p);
225 if (error)
226 return (error);
227
228 /* Release our reference to the vnode. (namei) */
229 vrele(nid.ni_vp);
230
231 /* Get table for the device. */
232 /*
233 * XXX: va_fsid is long (32/64 bits) and veriexec_tblfind()
234 * XXX: is passed a dev_t - uint32_t.
235 */
236 tbl = veriexec_tblfind((dev_t)va.va_fsid);
237 if (tbl == NULL) {
238 return (EINVAL);
239 }
240
241 /*
242 * XXX: Both va_fsid and va_fileid are long (32/64 bits), while
243 * XXX: veriexec_lookup() is passed dev_t and ino_t - uint32_t.
244 */
245 hh = veriexec_lookup((dev_t)va.va_fsid, (ino_t)va.va_fileid);
246 if (hh != NULL) {
247 /*
248 * Duplicate entry means something is wrong in
249 * the signature file. Just give collision info
250 * and return.
251 */
252 printf("veriexec: Duplicate entry. [%s, %ld:%ld] "
253 "old[type=0x%02x, algorithm=%s], "
254 "new[type=0x%02x, algorithm=%s] "
255 "(%s fingerprint)\n",
256 params->file, va.va_fsid, va.va_fileid,
257 hh->type, hh->ops->type,
258 params->type, params->fp_type,
259 (((hh->ops->hash_len != params->size) ||
260 (memcmp(hh->fp, params->fingerprint,
261 min(hh->ops->hash_len, params->size))
262 != 0)) ? "different" : "same"));
263
264 return (0);
265 }
266
267 e = malloc(sizeof(*e), M_TEMP, M_WAITOK);
268 /* XXX: va_fileid is long (32/64 bits), ino_t is uint32_t. */
269 e->inode = (ino_t)va.va_fileid;
270 e->type = params->type;
271 e->status = FINGERPRINT_NOTEVAL;
272 if ((e->ops = veriexec_find_ops(params->fp_type)) == NULL) {
273 free(e, M_TEMP);
274 printf("Veriexec: veriexecioctl: Invalid or unknown "
275 "fingerprint type \"%s\" for file \"%s\" "
276 "(dev=%ld, inode=%ld)\n", params->fp_type,
277 params->file, va.va_fsid, va.va_fileid);
278 return(EINVAL);
279 }
280
281 /*
282 * Just a bit of a sanity check - require the size of
283 * the fp to be passed in, check this against the expected
284 * size. Of course userland could lie deliberately, this
285 * really only protects against the obvious fumble of
286 * changing the fp type but not updating the fingerprint
287 * string.
288 */
289 if (e->ops->hash_len != params->size) {
290 printf("Veriexec: veriexecioctl: Inconsistent "
291 "fingerprint size for type \"%s\" for file "
292 "\"%s\" (dev=%ld, inode=%ld), size was %u "
293 "was expecting %zu\n", params->fp_type,
294 params->file, va.va_fsid, va.va_fileid,
295 params->size, e->ops->hash_len);
296 free(e, M_TEMP);
297 return(EINVAL);
298 }
299
300 e->fp = malloc(e->ops->hash_len, M_TEMP, M_WAITOK);
301 memcpy(e->fp, params->fingerprint, e->ops->hash_len);
302
303 veriexec_report("New entry.", params->file, &va, NULL,
304 REPORT_VERBOSE_HIGH, REPORT_NOALARM,
305 REPORT_NOPANIC);
306
307 error = veriexec_hashadd(tbl, e);
308
309 break;
310 }
311
312 default:
313 /* Invalid operation. */
314 error = ENODEV;
315
316 break;
317 }
318
319 return (error);
320 }
321
322 #if defined(__FreeBSD__)
323 static void
324 veriexec_drvinit(void *unused __unused)
325 {
326 make_dev(&verifiedexec_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
327 "veriexec");
328 verifiedexecattach(0, 0, 0);
329 }
330
331 SYSINIT(veriexec, SI_SUB_PSEUDO, SI_ORDER_ANY, veriexec_drvinit, NULL);
332 #endif
Cache object: 23fdbf326cb74f14e58603685bdbdc79
|