github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/cmd/snap-confine/snap-confine-args.c (about)

     1  /*
     2   * Copyright (C) 2016 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  
    18  #include "snap-confine-args.h"
    19  
    20  #include <string.h>
    21  
    22  #include "../libsnap-confine-private/utils.h"
    23  #include "../libsnap-confine-private/string-utils.h"
    24  #include "../libsnap-confine-private/test-utils.h"
    25  
    26  struct sc_args {
    27  	// The security tag that the application is intended to run with
    28  	char *security_tag;
    29  	// The executable that should be invoked
    30  	char *executable;
    31  	// Name of the base snap to use.
    32  	char *base_snap;
    33  
    34  	// Flag indicating that --version was passed on command line.
    35  	bool is_version_query;
    36  	// Flag indicating that --classic was passed on command line.
    37  	bool is_classic_confinement;
    38  };
    39  
    40  struct sc_args *sc_nonfatal_parse_args(int *argcp, char ***argvp,
    41  				       sc_error ** errorp)
    42  {
    43  	struct sc_args *args = NULL;
    44  	sc_error *err = NULL;
    45  
    46  	if (argcp == NULL || argvp == NULL) {
    47  		err = sc_error_init(SC_ARGS_DOMAIN, 0,
    48  				    "cannot parse arguments, argcp or argvp is NULL");
    49  		goto out;
    50  	}
    51  	// Use dereferenced versions of argcp and argvp for convenience.
    52  	int argc = *argcp;
    53  	char **const argv = *argvp;
    54  
    55  	if (argc == 0 || argv == NULL) {
    56  		err = sc_error_init(SC_ARGS_DOMAIN, 0,
    57  				    "cannot parse arguments, argc is zero or argv is NULL");
    58  		goto out;
    59  	}
    60  	// Sanity check, look for NULL argv entries.
    61  	for (int i = 0; i < argc; ++i) {
    62  		if (argv[i] == NULL) {
    63  			err = sc_error_init(SC_ARGS_DOMAIN, 0,
    64  					    "cannot parse arguments, argument at index %d is NULL",
    65  					    i);
    66  			goto out;
    67  		}
    68  	}
    69  
    70  	args = calloc(1, sizeof *args);
    71  	if (args == NULL) {
    72  		die("cannot allocate memory for command line arguments object");
    73  	}
    74  	// Check if we're being called through the ubuntu-core-launcher symlink.
    75  	// When this happens we want to skip the first positional argument as it is
    76  	// the security tag repeated (legacy).
    77  	bool ignore_first_tag = false;
    78  	char *basename = strrchr(argv[0], '/');
    79  	if (basename != NULL) {
    80  		// NOTE: this is safe because we, at most, may move to the NUL byte
    81  		// that compares to an empty string.
    82  		basename += 1;
    83  		if (strcmp(basename, "ubuntu-core-launcher") == 0) {
    84  			ignore_first_tag = true;
    85  		}
    86  	}
    87  	// Parse option switches.
    88  	int optind;
    89  	for (optind = 1; optind < argc; ++optind) {
    90  		// Look at all the options switches that start with the minus sign ('-')
    91  		if (argv[optind][0] != '-') {
    92  			// On first non-switch argument break the loop. The next loop looks
    93  			// just for non-option arguments. This ensures that options and
    94  			// positional arguments cannot be mixed.
    95  			break;
    96  		}
    97  		// Handle option switches
    98  		if (strcmp(argv[optind], "--version") == 0) {
    99  			args->is_version_query = true;
   100  			// NOTE: --version short-circuits the parser to finish
   101  			goto done;
   102  		} else if (strcmp(argv[optind], "--classic") == 0) {
   103  			args->is_classic_confinement = true;
   104  		} else if (strcmp(argv[optind], "--base") == 0) {
   105  			if (optind + 1 >= argc) {
   106  				err =
   107  				    sc_error_init(SC_ARGS_DOMAIN,
   108  						  SC_ARGS_ERR_USAGE,
   109  						  "Usage: snap-confine <security-tag> <executable>\n"
   110  						  "\n"
   111  						  "the --base option requires an argument");
   112  				goto out;
   113  			}
   114  			if (args->base_snap != NULL) {
   115  				err =
   116  				    sc_error_init(SC_ARGS_DOMAIN,
   117  						  SC_ARGS_ERR_USAGE,
   118  						  "Usage: snap-confine <security-tag> <executable>\n"
   119  						  "\n"
   120  						  "the --base option can be used only once");
   121  				goto out;
   122  
   123  			}
   124  			args->base_snap = sc_strdup(argv[optind + 1]);
   125  			optind += 1;
   126  		} else {
   127  			// Report unhandled option switches
   128  			err = sc_error_init(SC_ARGS_DOMAIN, SC_ARGS_ERR_USAGE,
   129  					    "Usage: snap-confine <security-tag> <executable>\n"
   130  					    "\n"
   131  					    "unrecognized command line option: %s",
   132  					    argv[optind]);
   133  			goto out;
   134  		}
   135  	}
   136  
   137  	// Parse positional arguments.
   138  	//
   139  	// NOTE: optind is not reset, we just continue from where we left off in
   140  	// the loop above.
   141  	for (; optind < argc; ++optind) {
   142  		if (args->security_tag == NULL) {
   143  			// The first positional argument becomes the security tag.
   144  			if (ignore_first_tag) {
   145  				// Unless we are called as ubuntu-core-launcher, then we just
   146  				// swallow and ignore that security tag altogether.
   147  				ignore_first_tag = false;
   148  				continue;
   149  			}
   150  			args->security_tag = sc_strdup(argv[optind]);
   151  		} else if (args->executable == NULL) {
   152  			// The second positional argument becomes the executable name.
   153  			args->executable = sc_strdup(argv[optind]);
   154  			// No more positional arguments are required.
   155  			// Stop the parsing process.
   156  			break;
   157  		}
   158  	}
   159  
   160  	// Verify that all mandatory positional arguments are present.
   161  	// Ensure that we have the security tag
   162  	if (args->security_tag == NULL) {
   163  		err = sc_error_init(SC_ARGS_DOMAIN, SC_ARGS_ERR_USAGE,
   164  				    "Usage: snap-confine <security-tag> <executable>\n"
   165  				    "\n"
   166  				    "application or hook security tag was not provided");
   167  		goto out;
   168  	}
   169  	// Ensure that we have the executable name
   170  	if (args->executable == NULL) {
   171  		err = sc_error_init(SC_ARGS_DOMAIN, SC_ARGS_ERR_USAGE,
   172  				    "Usage: snap-confine <security-tag> <executable>\n"
   173  				    "\n" "executable name was not provided");
   174  		goto out;
   175  	}
   176  
   177  	int i;
   178   done:
   179  	// "shift" the argument vector left, except for argv[0], to "consume" the
   180  	// arguments that were scanned / parsed correctly.
   181  	for (i = 1; optind + i < argc; ++i) {
   182  		argv[i] = argv[optind + i];
   183  	}
   184  	argv[i] = NULL;
   185  
   186  	// Write the updated argc back, argv is never modified.
   187  	*argcp = argc - optind;
   188  
   189   out:
   190  	// Don't return anything in case of an error.
   191  	if (err != NULL) {
   192  		sc_cleanup_args(&args);
   193  	}
   194  	// Forward the error and return
   195  	sc_error_forward(errorp, err);
   196  	return args;
   197  }
   198  
   199  void sc_args_free(struct sc_args *args)
   200  {
   201  	if (args != NULL) {
   202  		free(args->security_tag);
   203  		args->security_tag = NULL;
   204  		free(args->executable);
   205  		args->executable = NULL;
   206  		free(args->base_snap);
   207  		args->base_snap = NULL;
   208  		free(args);
   209  	}
   210  }
   211  
   212  void sc_cleanup_args(struct sc_args **ptr)
   213  {
   214  	sc_args_free(*ptr);
   215  	*ptr = NULL;
   216  }
   217  
   218  bool sc_args_is_version_query(const struct sc_args *args)
   219  {
   220  	if (args == NULL) {
   221  		die("cannot obtain version query flag from NULL argument parser");
   222  	}
   223  	return args->is_version_query;
   224  }
   225  
   226  bool sc_args_is_classic_confinement(const struct sc_args *args)
   227  {
   228  	if (args == NULL) {
   229  		die("cannot obtain classic confinement flag from NULL argument parser");
   230  	}
   231  	return args->is_classic_confinement;
   232  }
   233  
   234  const char *sc_args_security_tag(const struct sc_args *args)
   235  {
   236  	if (args == NULL) {
   237  		die("cannot obtain security tag from NULL argument parser");
   238  	}
   239  	return args->security_tag;
   240  }
   241  
   242  const char *sc_args_executable(const struct sc_args *args)
   243  {
   244  	if (args == NULL) {
   245  		die("cannot obtain executable from NULL argument parser");
   246  	}
   247  	return args->executable;
   248  }
   249  
   250  const char *sc_args_base_snap(const struct sc_args *args)
   251  {
   252  	if (args == NULL) {
   253  		die("cannot obtain base snap name from NULL argument parser");
   254  	}
   255  	return args->base_snap;
   256  }