github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/cmd/libsnap-confine-private/snap.c (about) 1 /* 2 * Copyright (C) 2015 Canonical Ltd 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 3 as 6 * published by the Free Software Foundation. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. 15 * 16 */ 17 #include "config.h" 18 #include "snap.h" 19 20 #include <errno.h> 21 #include <regex.h> 22 #include <stddef.h> 23 #include <stdlib.h> 24 #include <string.h> 25 #include <ctype.h> 26 27 #include "utils.h" 28 #include "string-utils.h" 29 #include "cleanup-funcs.h" 30 31 bool verify_security_tag(const char *security_tag, const char *snap_name) 32 { 33 const char *whitelist_re = 34 "^snap\\.([a-z0-9](-?[a-z0-9])*(_[a-z0-9]{1,10})?)\\.([a-zA-Z0-9](-?[a-zA-Z0-9])*|hook\\.[a-z](-?[a-z0-9])*)$"; 35 regex_t re; 36 if (regcomp(&re, whitelist_re, REG_EXTENDED) != 0) 37 die("can not compile regex %s", whitelist_re); 38 39 // first capture is for verifying the full security tag, second capture 40 // for verifying the snap_name is correct for this security tag 41 regmatch_t matches[2]; 42 int status = 43 regexec(&re, security_tag, sizeof matches / sizeof *matches, 44 matches, 0); 45 regfree(&re); 46 47 // Fail if no match or if snap name wasn't captured in the 2nd match group 48 if (status != 0 || matches[1].rm_so < 0) { 49 return false; 50 } 51 52 size_t len = matches[1].rm_eo - matches[1].rm_so; 53 return len == strlen(snap_name) 54 && strncmp(security_tag + matches[1].rm_so, snap_name, len) == 0; 55 } 56 57 bool sc_is_hook_security_tag(const char *security_tag) 58 { 59 const char *whitelist_re = 60 "^snap\\.[a-z](-?[a-z0-9])*(_[a-z0-9]{1,10})?\\.(hook\\.[a-z](-?[a-z])*)$"; 61 62 regex_t re; 63 if (regcomp(&re, whitelist_re, REG_EXTENDED | REG_NOSUB) != 0) 64 die("can not compile regex %s", whitelist_re); 65 66 int status = regexec(&re, security_tag, 0, NULL, 0); 67 regfree(&re); 68 69 return status == 0; 70 } 71 72 static int skip_lowercase_letters(const char **p) 73 { 74 int skipped = 0; 75 for (const char *c = *p; *c >= 'a' && *c <= 'z'; ++c) { 76 skipped += 1; 77 } 78 *p = (*p) + skipped; 79 return skipped; 80 } 81 82 static int skip_digits(const char **p) 83 { 84 int skipped = 0; 85 for (const char *c = *p; *c >= '0' && *c <= '9'; ++c) { 86 skipped += 1; 87 } 88 *p = (*p) + skipped; 89 return skipped; 90 } 91 92 static int skip_one_char(const char **p, char c) 93 { 94 if (**p == c) { 95 *p += 1; 96 return 1; 97 } 98 return 0; 99 } 100 101 void sc_instance_name_validate(const char *instance_name, sc_error ** errorp) 102 { 103 // NOTE: This function should be synchronized with the two other 104 // implementations: validate_instance_name and snap.ValidateInstanceName. 105 sc_error *err = NULL; 106 107 // Ensure that name is not NULL 108 if (instance_name == NULL) { 109 err = 110 sc_error_init(SC_SNAP_DOMAIN, SC_SNAP_INVALID_INSTANCE_NAME, 111 "snap instance name cannot be NULL"); 112 goto out; 113 } 114 // instance name length + 1 extra overflow + 1 NULL 115 char s[SNAP_INSTANCE_LEN + 1 + 1] = { 0 }; 116 strncpy(s, instance_name, sizeof(s) - 1); 117 118 char *t = s; 119 const char *snap_name = strsep(&t, "_"); 120 const char *instance_key = strsep(&t, "_"); 121 const char *third_separator = strsep(&t, "_"); 122 if (third_separator != NULL) { 123 err = 124 sc_error_init(SC_SNAP_DOMAIN, SC_SNAP_INVALID_INSTANCE_NAME, 125 "snap instance name can contain only one underscore"); 126 goto out; 127 } 128 129 sc_snap_name_validate(snap_name, &err); 130 if (err != NULL) { 131 goto out; 132 } 133 // When the instance_name is a normal snap name, instance_key will be 134 // NULL, so only validate instance_key when we found one. 135 if (instance_key != NULL) { 136 sc_instance_key_validate(instance_key, &err); 137 } 138 139 out: 140 sc_error_forward(errorp, err); 141 } 142 143 void sc_instance_key_validate(const char *instance_key, sc_error ** errorp) 144 { 145 // NOTE: see snap.ValidateInstanceName for reference of a valid instance key 146 // format 147 sc_error *err = NULL; 148 149 // Ensure that name is not NULL 150 if (instance_key == NULL) { 151 err = sc_error_init(SC_SNAP_DOMAIN, SC_SNAP_INVALID_NAME, 152 "instance key cannot be NULL"); 153 goto out; 154 } 155 // This is a regexp-free routine hand-coding the following pattern: 156 // 157 // "^[a-z]{1,10}$" 158 // 159 // The only motivation for not using regular expressions is so that we don't 160 // run untrusted input against a potentially complex regular expression 161 // engine. 162 int i = 0; 163 for (i = 0; instance_key[i] != '\0'; i++) { 164 if (islower(instance_key[i]) || isdigit(instance_key[i])) { 165 continue; 166 } 167 err = 168 sc_error_init(SC_SNAP_DOMAIN, SC_SNAP_INVALID_INSTANCE_KEY, 169 "instance key must use lower case letters or digits"); 170 goto out; 171 } 172 173 if (i == 0) { 174 err = 175 sc_error_init(SC_SNAP_DOMAIN, SC_SNAP_INVALID_INSTANCE_KEY, 176 "instance key must contain at least one letter or digit"); 177 } else if (i > SNAP_INSTANCE_KEY_LEN) { 178 err = 179 sc_error_init(SC_SNAP_DOMAIN, SC_SNAP_INVALID_INSTANCE_KEY, 180 "instance key must be shorter than 10 characters"); 181 } 182 out: 183 sc_error_forward(errorp, err); 184 } 185 186 void sc_snap_name_validate(const char *snap_name, sc_error ** errorp) 187 { 188 // NOTE: This function should be synchronized with the two other 189 // implementations: validate_snap_name and snap.ValidateName. 190 sc_error *err = NULL; 191 192 // Ensure that name is not NULL 193 if (snap_name == NULL) { 194 err = sc_error_init(SC_SNAP_DOMAIN, SC_SNAP_INVALID_NAME, 195 "snap name cannot be NULL"); 196 goto out; 197 } 198 // This is a regexp-free routine hand-codes the following pattern: 199 // 200 // "^([a-z0-9]+-?)*[a-z](-?[a-z0-9])*$" 201 // 202 // The only motivation for not using regular expressions is so that we 203 // don't run untrusted input against a potentially complex regular 204 // expression engine. 205 const char *p = snap_name; 206 if (skip_one_char(&p, '-')) { 207 err = sc_error_init(SC_SNAP_DOMAIN, SC_SNAP_INVALID_NAME, 208 "snap name cannot start with a dash"); 209 goto out; 210 } 211 bool got_letter = false; 212 int n = 0, m; 213 for (; *p != '\0';) { 214 if ((m = skip_lowercase_letters(&p)) > 0) { 215 n += m; 216 got_letter = true; 217 continue; 218 } 219 if ((m = skip_digits(&p)) > 0) { 220 n += m; 221 continue; 222 } 223 if (skip_one_char(&p, '-') > 0) { 224 n++; 225 if (*p == '\0') { 226 err = 227 sc_error_init(SC_SNAP_DOMAIN, 228 SC_SNAP_INVALID_NAME, 229 "snap name cannot end with a dash"); 230 goto out; 231 } 232 if (skip_one_char(&p, '-') > 0) { 233 err = 234 sc_error_init(SC_SNAP_DOMAIN, 235 SC_SNAP_INVALID_NAME, 236 "snap name cannot contain two consecutive dashes"); 237 goto out; 238 } 239 continue; 240 } 241 err = sc_error_init(SC_SNAP_DOMAIN, SC_SNAP_INVALID_NAME, 242 "snap name must use lower case letters, digits or dashes"); 243 goto out; 244 } 245 if (!got_letter) { 246 err = sc_error_init(SC_SNAP_DOMAIN, SC_SNAP_INVALID_NAME, 247 "snap name must contain at least one letter"); 248 goto out; 249 } 250 if (n < 2) { 251 err = sc_error_init(SC_SNAP_DOMAIN, SC_SNAP_INVALID_NAME, 252 "snap name must be longer than 1 character"); 253 goto out; 254 } 255 if (n > SNAP_NAME_LEN) { 256 err = sc_error_init(SC_SNAP_DOMAIN, SC_SNAP_INVALID_NAME, 257 "snap name must be shorter than 40 characters"); 258 goto out; 259 } 260 261 out: 262 sc_error_forward(errorp, err); 263 } 264 265 void sc_snap_drop_instance_key(const char *instance_name, char *snap_name, 266 size_t snap_name_size) 267 { 268 sc_snap_split_instance_name(instance_name, snap_name, snap_name_size, 269 NULL, 0); 270 } 271 272 void sc_snap_split_instance_name(const char *instance_name, char *snap_name, 273 size_t snap_name_size, char *instance_key, 274 size_t instance_key_size) 275 { 276 if (instance_name == NULL) { 277 die("internal error: cannot split instance name when it is unset"); 278 } 279 if (snap_name == NULL && instance_key == NULL) { 280 die("internal error: cannot split instance name when both snap name and instance key are unset"); 281 } 282 283 const char *pos = strchr(instance_name, '_'); 284 const char *instance_key_start = ""; 285 size_t snap_name_len = 0; 286 size_t instance_key_len = 0; 287 if (pos == NULL) { 288 snap_name_len = strlen(instance_name); 289 } else { 290 snap_name_len = pos - instance_name; 291 instance_key_start = pos + 1; 292 instance_key_len = strlen(instance_key_start); 293 } 294 295 if (snap_name != NULL) { 296 if (snap_name_len >= snap_name_size) { 297 die("snap name buffer too small"); 298 } 299 300 memcpy(snap_name, instance_name, snap_name_len); 301 snap_name[snap_name_len] = '\0'; 302 } 303 304 if (instance_key != NULL) { 305 if (instance_key_len >= instance_key_size) { 306 die("instance key buffer too small"); 307 } 308 memcpy(instance_key, instance_key_start, instance_key_len); 309 instance_key[instance_key_len] = '\0'; 310 } 311 }