1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2021 OmniOS Community Edition (OmniOSce) Association. 14 */ 15 16 /* 17 * Two privilege sets are maintained. One which is the minimal privileges 18 * necessary to run bhyve - 'bhyve_priv_min' - and one which is the maximum 19 * privileges required - 'bhyve_priv_max'. This second set starts off identical 20 * to the first, and is added to by modules during initialisation depending on 21 * their requirements and their configuration. 22 * Once all modules are initialised, and before the VM is set running, the 23 * privileges are locked by setting the 'Permitted' privilege set from the 24 * contents of the maximum set, which is now the upper bound, and dropping back 25 * to the minimum set. 26 * 27 * Privileges are process wide, and this framework does not support threaded 28 * access. The ID of the thread that first initialises privileges is recorded, 29 * and all subsequent privilege operations must be done by the same thread. 30 */ 31 32 #include <err.h> 33 #include <pthread.h> 34 #include <priv.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <upanic.h> 38 #include <sys/debug.h> 39 #include "config.h" 40 #include "debug.h" 41 #include "privileges.h" 42 43 static pthread_t priv_thread; 44 static priv_set_t *bhyve_priv_init; 45 static priv_set_t *bhyve_priv_min; 46 static priv_set_t *bhyve_priv_max; 47 static bool priv_debug = false; 48 49 #define DPRINTF(params) \ 50 if (priv_debug) do { PRINTLN params; fflush(stdout); } while (0) 51 52 static void 53 illumos_priv_printset(const char *tag, priv_set_t *set) 54 { 55 char *s; 56 57 s = priv_set_to_str(set, ',', PRIV_STR_LIT); 58 if (s == NULL) { 59 warn("priv_set_to_str(%s) failed", tag); 60 return; 61 } 62 DPRINTF((" + %s: %s", tag, s)); 63 free(s); 64 } 65 66 void 67 illumos_priv_reduce(void) 68 { 69 VERIFY3S(pthread_equal(pthread_self(), priv_thread), !=, 0); 70 DPRINTF((" + Reducing privileges to minimum")); 71 if (setppriv(PRIV_SET, PRIV_EFFECTIVE, bhyve_priv_min) != 0) 72 err(4, "failed to reduce privileges"); 73 } 74 75 static void 76 illumos_priv_add_set(priv_set_t *set, const char *priv, const char *src) 77 { 78 VERIFY3S(pthread_equal(pthread_self(), priv_thread), !=, 0); 79 DPRINTF((" + Adding privilege %s (%s)", priv, src)); 80 if (!priv_ismember(bhyve_priv_init, priv)) 81 errx(4, "Privilege %s (for %s) not in initial set", priv, src); 82 if (priv_addset(set, priv) != 0) 83 err(4, "Failed to add %s privilege for %s", priv, src); 84 } 85 void 86 illumos_priv_add(const char *priv, const char *src) 87 { 88 illumos_priv_add_set(bhyve_priv_max, priv, src); 89 } 90 91 void 92 illumos_priv_add_min(const char *priv, const char *src) 93 { 94 illumos_priv_add_set(bhyve_priv_min, priv, src); 95 illumos_priv_add_set(bhyve_priv_max, priv, src); 96 } 97 98 void 99 illumos_priv_init(void) 100 { 101 priv_debug = get_config_bool_default("privileges.debug", false); 102 103 DPRINTF((" + Initialising privileges.")); 104 105 if (priv_debug) 106 (void) setpflags(PRIV_DEBUG, 1); 107 108 priv_thread = pthread_self(); 109 110 if ((bhyve_priv_init = priv_allocset()) == NULL) 111 err(4, "failed to allocate memory for initial priv set"); 112 if ((bhyve_priv_min = priv_allocset()) == NULL) 113 err(4, "failed to allocate memory for minimum priv set"); 114 if ((bhyve_priv_max = priv_allocset()) == NULL) 115 err(4, "failed to allocate memory for maximum priv set"); 116 117 if (getppriv(PRIV_EFFECTIVE, bhyve_priv_init) != 0) 118 err(4, "failed to fetch current privileges"); 119 120 if (priv_debug) 121 illumos_priv_printset("initial", bhyve_priv_init); 122 123 /* 124 * file_read is left in the minimum set to allow for lazy library 125 * loading. 126 */ 127 priv_basicset(bhyve_priv_min); 128 VERIFY0(priv_delset(bhyve_priv_min, PRIV_FILE_LINK_ANY)); 129 VERIFY0(priv_delset(bhyve_priv_min, PRIV_FILE_WRITE)); 130 VERIFY0(priv_delset(bhyve_priv_min, PRIV_NET_ACCESS)); 131 VERIFY0(priv_delset(bhyve_priv_min, PRIV_PROC_EXEC)); 132 VERIFY0(priv_delset(bhyve_priv_min, PRIV_PROC_FORK)); 133 VERIFY0(priv_delset(bhyve_priv_min, PRIV_PROC_INFO)); 134 VERIFY0(priv_delset(bhyve_priv_min, PRIV_PROC_SECFLAGS)); 135 VERIFY0(priv_delset(bhyve_priv_min, PRIV_PROC_SESSION)); 136 137 priv_intersect(bhyve_priv_init, bhyve_priv_min); 138 priv_copyset(bhyve_priv_min, bhyve_priv_max); 139 140 /* 141 * These are privileges that we know will always be needed. 142 * Other privileges may be added by modules as necessary during 143 * initialisation. 144 */ 145 146 illumos_priv_add(PRIV_FILE_WRITE, "init"); 147 148 /* 149 * bhyve can work without proc_clock_highres so don't enforce that 150 * it is present. 151 */ 152 if (priv_ismember(bhyve_priv_init, PRIV_PROC_CLOCK_HIGHRES)) 153 illumos_priv_add_min(PRIV_PROC_CLOCK_HIGHRES, "init"); 154 else 155 warnx("The 'proc_clock_highres' privilege is not available"); 156 } 157 158 void 159 illumos_priv_lock(void) 160 { 161 VERIFY3S(pthread_equal(pthread_self(), priv_thread), !=, 0); 162 163 if (bhyve_priv_init == NULL) { 164 const char *msg = "attempted to re-lock privileges"; 165 upanic(msg, strlen(msg)); 166 } 167 168 priv_intersect(bhyve_priv_init, bhyve_priv_max); 169 170 if (priv_debug) { 171 DPRINTF((" + Locking privileges")); 172 173 illumos_priv_printset("min", bhyve_priv_min); 174 illumos_priv_printset("max", bhyve_priv_max); 175 } 176 177 if (setppriv(PRIV_SET, PRIV_PERMITTED, bhyve_priv_max) != 0) { 178 const char *fail = "failed to reduce permitted privileges"; 179 upanic(fail, strlen(fail)); 180 } 181 182 if (setppriv(PRIV_SET, PRIV_LIMIT, bhyve_priv_max) != 0) { 183 const char *fail = "failed to reduce limit privileges"; 184 upanic(fail, strlen(fail)); 185 } 186 187 illumos_priv_reduce(); 188 189 priv_freeset(bhyve_priv_init); 190 bhyve_priv_init = NULL; 191 } 192