]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/security/mac_veriexec/veriexec_fingerprint.c
mac_veriexec_fingerprint_check_vnode: v_writecount > 0 means active writers
[FreeBSD/FreeBSD.git] / sys / security / mac_veriexec / veriexec_fingerprint.c
1 /*
2  * $FreeBSD$
3  *
4  * Copyright (c) 2011, 2012, 2013, 2015, 2016, Juniper Networks, Inc.
5  * All rights reserved.
6  *
7  * Originally derived from:
8  *      $NetBSD: kern_verifiedexec.c,v 1.7 2003/11/18 13:13:03 martin Exp $
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. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31
32 #include <sys/cdefs.h>
33
34 #include "opt_mac.h"
35
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/imgact.h>
39 #include <sys/jail.h>
40 #include <sys/kernel.h>
41 #include <sys/lock.h>
42 #include <sys/malloc.h>
43 #include <sys/mount.h> 
44 #include <sys/mutex.h>
45 #include <sys/proc.h>
46 #include <sys/sbuf.h>
47 #include <sys/syslog.h>
48 #include <sys/vnode.h>
49
50 #include "mac_veriexec.h"
51 #include "mac_veriexec_internal.h"
52
53 /**
54  * @var fpops_list
55  * @internal
56  * @brief Fingerprint operations list
57  *
58  * This is essentially the list of fingerprint modules currently loaded
59  */
60 static LIST_HEAD(fpopshead, mac_veriexec_fpops) fpops_list;
61
62 static int mac_veriexec_late;
63
64 static int sysctl_mac_veriexec_algorithms(SYSCTL_HANDLER_ARGS);
65
66 SYSCTL_PROC(_security_mac_veriexec, OID_AUTO, algorithms,
67     CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
68     0, 0, sysctl_mac_veriexec_algorithms, "A",
69     "Verified execution supported hashing algorithms");
70
71 static int
72 sysctl_mac_veriexec_algorithms(SYSCTL_HANDLER_ARGS)
73 {
74         struct sbuf sb;
75         struct mac_veriexec_fpops *fpops;
76         int algorithms, error;
77
78         algorithms = 0;
79         sbuf_new(&sb, NULL, 128, SBUF_AUTOEXTEND);
80         LIST_FOREACH(fpops, &fpops_list, entries) {
81                 if (algorithms++)
82                         sbuf_printf(&sb, " ");
83                 sbuf_printf(&sb, "%s", fpops->type);
84         }
85         sbuf_finish(&sb);
86         error = SYSCTL_OUT(req, sbuf_data(&sb), sbuf_len(&sb));
87         sbuf_delete(&sb);
88         return (error);
89 }
90
91 /**
92  * @internal
93  * @brief Consistently identify file encountering errors
94  *
95  * @param imgp          image params to display
96  * @param td            calling thread
97  * @param msg           message to display
98  *
99  * @return String form of the information stored in @p imgp
100  */
101 static void
102 identify_error (struct image_params *imgp, struct thread *td, const char *msg)
103 {
104         struct proc *parent;
105         pid_t ppid, gppid;
106
107         parent = imgp->proc->p_pptr;
108         ppid = (parent != NULL) ? parent->p_pid : 0;
109         gppid = (parent != NULL && parent->p_pptr != NULL) ?
110             parent->p_pptr->p_pid : 0;
111
112         log(LOG_ERR, MAC_VERIEXEC_FULLNAME ": %s (file=%s fsid=%ju fileid=%ju "
113             "gen=%lu uid=%u pid=%u ppid=%u gppid=%u)", msg,
114             (imgp->args != NULL) ? imgp->args->fname : "",
115             (uintmax_t)imgp->attr->va_fsid, (uintmax_t)imgp->attr->va_fileid,
116             imgp->attr->va_gen, td->td_ucred->cr_ruid, imgp->proc->p_pid,
117             ppid, gppid);
118 }
119
120 /**
121  * @internal
122  * @brief Check the fingerprint type for the given file and evaluate the
123  * fingerprint for that file.
124  *
125  * It is assumed that @p fingerprint has sufficient storage to hold the
126  * resulting fingerprint string.
127  *
128  * @param vp            vnode to check
129  * @param ip            file info from the meta-data store
130  * @param td            calling thread
131  * @param file_size     size of the file to read
132  * @param fingerprint   resulting fingerprint
133  *
134  * @return 0 on success, otherwise an error code.
135  */
136 static int
137 evaluate_fingerprint(struct vnode *vp, struct mac_veriexec_file_info *ip,
138     struct thread *td, off_t file_size, unsigned char *fingerprint)
139 {
140         uint8_t *filebuf;
141         void *ctx;
142         off_t offset;
143         size_t count, nread, resid;
144         int error = EINVAL;
145
146         filebuf = malloc(PAGE_SIZE, M_VERIEXEC, M_WAITOK);
147         ctx = malloc(ip->ops->context_size, M_VERIEXEC, M_WAITOK);
148
149         (ip->ops->init)(ctx);
150         for (offset = 0; offset < file_size; offset += nread) {
151                 if ((offset + PAGE_SIZE) > file_size)
152                         count = file_size - offset;
153                 else
154                         count = PAGE_SIZE;
155
156                 error = vn_rdwr_inchunks(UIO_READ, vp, filebuf, count, offset,
157                     UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, &resid,
158                     td);
159                 if (error)
160                         goto failed;
161
162                 nread = count - resid;
163                 (ip->ops->update)(ctx, filebuf, nread);
164         }
165         (ip->ops->final)(fingerprint, ctx);
166
167 #ifdef DEBUG_VERIEXEC_FINGERPRINT
168         for (offset = 0; offset < ip->ops->digest_len; offset++)
169                 printf("%02x", fingerprint[offset]);
170         printf("\n");
171 #endif
172
173 failed:
174         free(ctx, M_VERIEXEC);
175         free(filebuf, M_VERIEXEC);
176         return (error);
177 }
178
179 /**
180  * @internal
181  * @brief Compare the two given fingerprints to see if they are the same.
182  *
183  * Differing fingerprint methods may have differing lengths which
184  * is handled by this routine.
185  *
186  * @param ip            file info from the meta-data store
187  * @param digest        digest to compare
188  *
189  * @return 0 if the fingerprints match and non-zero if they do not.
190  */
191 static int
192 fingerprintcmp(struct mac_veriexec_file_info *ip, unsigned char *digest)
193 {
194
195         return memcmp(ip->fingerprint, digest, ip->ops->digest_len);
196 }
197
198 /**
199  * @brief Check if @p fingerprint matches the one associated with the vnode
200  *     @p vp
201  *
202  * @param vp            vnode to check
203  * @param ip            file info from the meta-data store
204  * @param td            calling thread
205  * @param file_size     size of the file to read
206  * @param fingerprint   fingerprint to compare
207  *
208  * @return 0 if they match, otherwise an error code.
209  */
210 int
211 mac_veriexec_fingerprint_check_vnode(struct vnode *vp,
212     struct mac_veriexec_file_info *ip, struct thread *td, off_t file_size,
213     unsigned char *fingerprint)
214 {
215         int error;
216
217         /* reject fingerprint if writers are active */
218         if (vp->v_writecount > 0)
219                 return (ETXTBSY);
220
221         if ((vp->v_mount->mnt_flag & MNT_VERIFIED) != 0) {
222                 VERIEXEC_DEBUG(2, ("file %ju.%lu on verified %s mount\n",
223                     (uintmax_t)ip->fileid, ip->gen,
224                     vp->v_mount->mnt_vfc->vfc_name));
225
226                 /*
227                  * The VFS is backed by a file which has been verified.
228                  * No need to waste time here.
229                  */
230                 return (0);
231         }
232
233         error = evaluate_fingerprint(vp, ip, td, file_size, fingerprint);
234         if (error)
235                 return (error);
236
237         if (fingerprintcmp(ip, fingerprint) != 0)
238                 return (EAUTH);
239
240         return (0);
241 }
242
243 /**
244  * @brief Check a file signature and validate it.
245  *
246  * @param imgp          parameters for the image to check
247  * @param check_files   if 1, check the files list first, otherwise check the
248  *                      exectuables list first
249  * @param td            calling thread
250  *
251  * @note Called with imgp->vp locked.
252  *
253  * @return 0 if the signature is valid, otherwise an error code.
254  */
255 int
256 mac_veriexec_fingerprint_check_image(struct image_params *imgp,
257     int check_files, struct thread *td)
258 {
259         struct vnode *vp = imgp->vp;
260         int error;
261         fingerprint_status_t status;
262
263         if (!mac_veriexec_in_state(VERIEXEC_STATE_ACTIVE))
264                 return 0;
265
266         error = mac_veriexec_metadata_fetch_fingerprint_status(vp, imgp->attr,
267             td, check_files);
268         if (error && error != EAUTH)
269                 return (error);
270
271         /*
272          * By now status is set.
273          */
274         status = mac_veriexec_get_fingerprint_status(vp);
275         switch (status) {
276         case FINGERPRINT_INVALID: /* should not happen */
277                 identify_error(imgp, td, "got unexpected FINGERPRINT_INVALID");
278                 error = EPERM;
279                 break;
280
281         case FINGERPRINT_FILE:
282                 if (!check_files) {
283                         if (prison0.pr_securelevel > 1 ||
284                             mac_veriexec_in_state(VERIEXEC_STATE_ENFORCE))
285                                 error = EPERM;
286                 }
287                 break;
288
289         case FINGERPRINT_VALID: /* is ok - report so if debug is on */
290                 VERIEXEC_DEBUG(4, ("Fingerprint matches\n"));
291                 break;
292
293         case FINGERPRINT_INDIRECT: /* fingerprint ok but need to check
294                                       for direct execution */
295                 if (!imgp->interpreted) {
296                         identify_error(imgp, td, "attempted direct execution");
297                         if (prison0.pr_securelevel > 1 ||
298                             mac_veriexec_in_state(VERIEXEC_STATE_ENFORCE))
299                                 error = EPERM;
300                 }
301                 break;
302
303         case FINGERPRINT_NOMATCH: /* does not match - whine about it */
304                 identify_error(imgp, td,
305                     "fingerprint does not match loaded value");
306                 if (prison0.pr_securelevel > 1 ||
307                     mac_veriexec_in_state(VERIEXEC_STATE_ENFORCE))
308                         error = EAUTH;
309                 break;
310
311         case FINGERPRINT_NOENTRY: /* no entry in the list, complain */
312                 identify_error(imgp, td, "no fingerprint");
313                 if (prison0.pr_securelevel > 1 ||
314                     mac_veriexec_in_state(VERIEXEC_STATE_ENFORCE))
315                         error = EAUTH;
316                 break;
317
318         case FINGERPRINT_NODEV: /* no signatures for the device, complain */
319                 identify_error(imgp, td, "no signatures for device");
320                 if (prison0.pr_securelevel > 1 ||
321                     mac_veriexec_in_state(VERIEXEC_STATE_ENFORCE))
322                         error = EAUTH;
323                 break;
324
325         default: /* this should never happen. */
326                 identify_error(imgp, td, "invalid status field for vnode");
327                 error = EPERM;
328         }
329         return error; 
330 }
331
332 /**
333  * @brief Look up the fingerprint operations for a specific digest type
334  *
335  * @return A pointer to fingerprint operations, if found, or else @c NULL.
336  */
337 struct mac_veriexec_fpops *
338 mac_veriexec_fingerprint_lookup_ops(const char *type)
339 {
340         struct mac_veriexec_fpops *fpops;
341
342         if (type == NULL)
343                 return (NULL);
344
345         LIST_FOREACH(fpops, &fpops_list, entries) {
346                 if (!strcasecmp(type, fpops->type))
347                         break;
348         }
349         return (fpops);
350 }
351
352 /**
353  * @brief Add fingerprint operations for a specific digest type
354  *
355  * Any attempts to add a duplicate digest type results in an error.
356  *
357  * @return 0 if the ops were added successfully, otherwise an error code.
358  */
359 int
360 mac_veriexec_fingerprint_add_ops(struct mac_veriexec_fpops *fpops)
361 {
362
363         /* Sanity check the ops */
364         if (fpops->type == NULL || fpops->digest_len == 0 ||
365             fpops->context_size == 0 || fpops->init == NULL ||
366             fpops->update == NULL || fpops->final == NULL)
367                 return (EINVAL);
368
369         /* Make sure we do not already have ops for this digest type */
370         if (mac_veriexec_fingerprint_lookup_ops(fpops->type))
371                 return (EEXIST);
372
373         /* Add the ops to the list */
374         LIST_INSERT_HEAD(&fpops_list, fpops, entries);
375
376         printf("MAC/veriexec fingerprint module loaded: %s\n", fpops->type);
377
378         return (0);
379 }
380
381 /**
382  * @brief Initialize the fingerprint operations list
383  */
384 void
385 mac_veriexec_fingerprint_init(void)
386 {
387
388         LIST_INIT(&fpops_list);
389 }
390
391 /**
392  * @brief Handle fingerprint module events
393  *
394  * This function is called by the @c MAC_VERIEXEC_FPMOD macro.
395  *
396  * @param mod           module information
397  * @param type          event type
398  * @param data          event-specific data
399  *
400  * @return On @c MOD_LOAD, 0 if the fingerprint ops were added successfully,
401  *     otherwise an error code. All other event types result in an error code.
402  */
403 int
404 mac_veriexec_fingerprint_modevent(module_t mod, int type, void *data)
405 {
406         struct mac_veriexec_fpops *fpops;
407         int error;
408
409         error = 0;
410         fpops = (struct mac_veriexec_fpops *) data;
411
412         switch (type) {
413         case MOD_LOAD:
414                 /* We do not allow late loading of fingerprint modules */
415                 if (mac_veriexec_late) {
416                         printf("%s: can't load %s fingerprint module after "
417                             "booting\n", __func__, fpops->type);
418                         error = EBUSY;
419                         break;
420                 }
421                 error = mac_veriexec_fingerprint_add_ops(fpops);
422                 break;
423         case MOD_UNLOAD:
424                 error = EBUSY;
425                 break;
426         default:
427                 error = EOPNOTSUPP;
428                 break;
429         }
430
431         return (error);
432 }
433
434 /**
435  * @internal
436  * @brief Mark veriexec late initialization flag
437  */
438 static void
439 mac_veriexec_late_init(void)
440 {
441
442         mac_veriexec_late = 1;
443 }
444
445 SYSINIT(mac_veriexec_late, SI_SUB_MAC_LATE, SI_ORDER_ANY,
446     mac_veriexec_late_init, NULL);