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