github.com/rigado/snapd@v2.42.5-go-mod+incompatible/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  }