1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2021-2023, Juniper Networks, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 */ 29 30 #include <sys/cdefs.h> 31 #include <sys/types.h> 32 #include <sys/errno.h> 33 #include <sys/mac.h> 34 35 #include <unistd.h> 36 #include <string.h> 37 38 #include <security/mac_veriexec/mac_veriexec.h> 39 40 /** 41 * @brief get veriexec params for a process 42 * 43 * @return 44 * @li 0 if successful 45 */ 46 int 47 veriexec_get_pid_params(pid_t pid, 48 struct mac_veriexec_syscall_params *params) 49 { 50 struct mac_veriexec_syscall_params_args args; 51 52 if (params == NULL) 53 return EINVAL; 54 55 args.u.pid = pid; 56 args.params = params; 57 return mac_syscall(MAC_VERIEXEC_NAME, 58 MAC_VERIEXEC_GET_PARAMS_PID_SYSCALL, &args); 59 } 60 61 /** 62 * @brief get veriexec params for a path 63 * 64 * @return 65 * @li 0 if successful 66 */ 67 int 68 veriexec_get_path_params(const char *file, 69 struct mac_veriexec_syscall_params *params) 70 { 71 struct mac_veriexec_syscall_params_args args; 72 73 if (file == NULL || params == NULL) 74 return EINVAL; 75 76 args.u.filename = file; 77 args.params = params; 78 return mac_syscall(MAC_VERIEXEC_NAME, 79 MAC_VERIEXEC_GET_PARAMS_PATH_SYSCALL, &args); 80 } 81 82 /** 83 * @brief return label associated with a path 84 * 85 * @param[in] file 86 * pathname of file to lookup. 87 * 88 * @prarm[in] buf 89 * if not NULL and big enough copy label to buf. 90 * otherwise return a copy of label. 91 * 92 * @param[in] bufsz 93 * size of buf, must be greater than found label length. 94 * 95 * @return 96 * @li NULL if no label 97 * @li pointer to label 98 */ 99 char * 100 veriexec_get_path_label(const char *file, char *buf, size_t bufsz) 101 { 102 struct mac_veriexec_syscall_params params; 103 char *cp; 104 105 cp = NULL; 106 if (veriexec_get_path_params(file, ¶ms) == 0) { 107 /* Does label contain a label */ 108 if (params.labellen > 0) { 109 if (buf != NULL && bufsz > params.labellen) { 110 strlcpy(buf, params.label, bufsz); 111 cp = buf; 112 } else 113 cp = strdup(params.label); 114 } 115 } 116 return cp; 117 } 118 119 /** 120 * @brief return label of a process 121 * 122 * 123 * @param[in] pid 124 * process id of interest. 125 * 126 * @prarm[in] buf 127 * if not NULL and big enough copy label to buf. 128 * otherwise return a copy of label. 129 * 130 * @param[in] bufsz 131 * size of buf, must be greater than found label length. 132 * 133 * @return 134 * @li NULL if no label 135 * @li pointer to label 136 */ 137 char * 138 veriexec_get_pid_label(pid_t pid, char *buf, size_t bufsz) 139 { 140 struct mac_veriexec_syscall_params params; 141 char *cp; 142 143 cp = NULL; 144 if (veriexec_get_pid_params(pid, ¶ms) == 0) { 145 /* Does label contain a label */ 146 if (params.labellen > 0) { 147 if (buf != NULL && bufsz > params.labellen) { 148 strlcpy(buf, params.label, bufsz); 149 cp = buf; 150 } else 151 cp = strdup(params.label); 152 } 153 } 154 return cp; 155 } 156 157 /* 158 * we match 159 * ^want$ 160 * ^want, 161 * ,want, 162 * ,want$ 163 * 164 * and if want ends with / then we match that prefix too. 165 */ 166 static int 167 check_label_want(const char *label, size_t labellen, 168 const char *want, size_t wantlen) 169 { 170 char *cp; 171 172 /* Does label contain [,]<want>[,] ? */ 173 if (labellen > 0 && wantlen > 0 && 174 (cp = strstr(label, want)) != NULL) { 175 if (cp == label || cp[-1] == ',') { 176 if (cp[wantlen] == '\0' || cp[wantlen] == ',' || 177 (cp[wantlen-1] == '/' && want[wantlen-1] == '/')) 178 return 1; /* yes */ 179 } 180 } 181 return 0; /* no */ 182 } 183 184 /** 185 * @brief check if a process has label that contains what we want 186 * 187 * @param[in] pid 188 * process id of interest. 189 * 190 * @param[in] want 191 * the label we are looking for 192 * if want ends with ``/`` it is assumed a prefix 193 * otherwise we expect it to be followed by ``,`` or end of string. 194 * 195 * @return 196 * @li 0 if no 197 * @li 1 if yes 198 */ 199 int 200 veriexec_check_pid_label(pid_t pid, const char *want) 201 { 202 struct mac_veriexec_syscall_params params; 203 size_t n; 204 205 if (want != NULL && 206 (n = strlen(want)) > 0 && 207 veriexec_get_pid_params(pid, ¶ms) == 0) { 208 return check_label_want(params.label, params.labellen, 209 want, n); 210 } 211 return 0; /* no */ 212 } 213 214 /** 215 * @brief check if a path has label that contains what we want 216 * 217 * @param[in] path 218 * pathname of interest. 219 * 220 * @param[in] want 221 * the label we are looking for 222 * if want ends with ``/`` it is assumed a prefix 223 * otherwise we expect it to be followed by ``,`` or end of string. 224 * 225 * @return 226 * @li 0 if no 227 * @li 1 if yes 228 */ 229 int 230 veriexec_check_path_label(const char *file, const char *want) 231 { 232 struct mac_veriexec_syscall_params params; 233 size_t n; 234 235 if (want != NULL && file != NULL && 236 (n = strlen(want)) > 0 && 237 veriexec_get_path_params(file, ¶ms) == 0) { 238 return check_label_want(params.label, params.labellen, 239 want, n); 240 } 241 return 0; /* no */ 242 } 243 244 #ifdef UNIT_TEST 245 #include <stdlib.h> 246 #include <stdio.h> 247 #include <err.h> 248 249 static char * 250 hash2hex(char *type, unsigned char *digest) 251 { 252 static char buf[2*MAXFINGERPRINTLEN+1]; 253 size_t n; 254 int i; 255 256 if (strcmp(type, "SHA1") == 0) { 257 n = 20; 258 } else if (strcmp(type, "SHA256") == 0) { 259 n = 32; 260 } else if (strcmp(type, "SHA384") == 0) { 261 n = 48; 262 } 263 for (i = 0; i < n; i++) { 264 sprintf(&buf[2*i], "%02x", (unsigned)digest[i]); 265 } 266 return buf; 267 } 268 269 int 270 main(int argc, char *argv[]) 271 { 272 struct mac_veriexec_syscall_params params; 273 pid_t pid; 274 char buf[BUFSIZ]; 275 const char *cp; 276 char *want = NULL; 277 int lflag = 0; 278 int pflag = 0; 279 int error; 280 int c; 281 282 while ((c = getopt(argc, argv, "lpw:")) != -1) { 283 switch (c) { 284 case 'l': 285 lflag = 1; 286 break; 287 case 'p': 288 pflag = 1; 289 break; 290 case 'w': 291 want = optarg; 292 break; 293 default: 294 break; 295 } 296 } 297 for (; optind < argc; optind++) { 298 299 if (pflag) { 300 pid = atoi(argv[optind]); 301 if (lflag) { 302 cp = veriexec_get_pid_label(pid, buf, sizeof(buf)); 303 if (cp) 304 printf("pid=%d label='%s'\n", pid, cp); 305 continue; 306 } 307 if (want) { 308 error = veriexec_check_pid_label(pid, want); 309 printf("pid=%d want='%s': %d\n", 310 pid, want, error); 311 continue; 312 } 313 error = veriexec_get_pid_params(pid, ¶ms); 314 } else { 315 if (lflag) { 316 cp = veriexec_get_path_label(argv[optind], 317 buf, sizeof(buf)); 318 if (cp) 319 printf("path='%s' label='%s'\n", 320 argv[optind], cp); 321 continue; 322 } 323 if (want) { 324 error = veriexec_check_path_label(argv[optind], want); 325 printf("path='%s' want='%s': %d\n", 326 argv[optind], want, error); 327 continue; 328 } 329 error = veriexec_get_path_params(argv[optind], ¶ms); 330 } 331 if (error) { 332 err(2, "%s, error=%d", argv[optind], error); 333 } 334 335 printf("arg=%s, type=%s, flags=%u, label='%s', fingerprint='%s'\n", 336 argv[optind], params.fp_type, (unsigned)params.flags, 337 params.label, 338 hash2hex(params.fp_type, params.fingerprint)); 339 } 340 return 0; 341 } 342 #endif 343