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 }