[8bd77b5] | 1 | |
---|
| 2 | /** |
---|
| 3 | ** abac_util.c |
---|
| 4 | **/ |
---|
| 5 | |
---|
[7f25a67f] | 6 | #include <err.h> |
---|
| 7 | #include <stdlib.h> |
---|
| 8 | #include <string.h> |
---|
[7727f26] | 9 | #include <stdio.h> |
---|
[8bd77b5] | 10 | #include <assert.h> |
---|
[2efdff5] | 11 | #include <stdarg.h> |
---|
| 12 | #include <sys/stat.h> |
---|
[ba6027a] | 13 | #include <termios.h> |
---|
| 14 | #include <unistd.h> |
---|
[8bd77b5] | 15 | |
---|
| 16 | #include "abac_util.h" |
---|
[7727f26] | 17 | |
---|
| 18 | static int debug=0; |
---|
[7f25a67f] | 19 | |
---|
[ba6027a] | 20 | /* Callback configuration */ |
---|
| 21 | struct cb_opts { |
---|
| 22 | bool use_prompt; /* Print a prompt to stderr */ |
---|
| 23 | bool use_echo; /* If true, turn off input echo on stdin */ |
---|
| 24 | unsigned int tries; /* Number of attempts allowed */ |
---|
| 25 | char prompt[20]; /* The prompt to display if use_echo is true */ |
---|
| 26 | }; |
---|
| 27 | |
---|
| 28 | |
---|
[8bd77b5] | 29 | #define SHA1_LENGTH 40 |
---|
[bea18ef] | 30 | #define ABAC_VERSION "0.2.2" |
---|
[8bd77b5] | 31 | |
---|
[2efdff5] | 32 | char *abac_version() |
---|
| 33 | { |
---|
[bea18ef] | 34 | if(debug) |
---|
[b92a620] | 35 | fprintf(stderr,"abac_version is %s\n", ABAC_VERSION); |
---|
[373bf68] | 36 | return ABAC_VERSION; |
---|
[2efdff5] | 37 | } |
---|
| 38 | |
---|
[ba6027a] | 39 | void abac_clean_string(char *string) |
---|
| 40 | { |
---|
| 41 | int len=strlen(string); |
---|
| 42 | int i; |
---|
| 43 | for(i=0;i<len;i++) |
---|
| 44 | string[i]=0; |
---|
| 45 | } |
---|
| 46 | |
---|
[2efdff5] | 47 | void abac_errx(int eval, const char *msg) |
---|
| 48 | { |
---|
| 49 | errx(eval,"%s",msg); |
---|
| 50 | } |
---|
[8bd77b5] | 51 | |
---|
[7f25a67f] | 52 | /** |
---|
| 53 | * Malloc, fatal on error. |
---|
| 54 | */ |
---|
[3c251d0] | 55 | void *abac_xmalloc(size_t size) { |
---|
[7f25a67f] | 56 | void *ret; |
---|
| 57 | |
---|
| 58 | ret = malloc(size); |
---|
| 59 | if (ret == NULL) |
---|
| 60 | err(1, "malloc"); |
---|
| 61 | |
---|
| 62 | return ret; |
---|
| 63 | } |
---|
| 64 | |
---|
[8bd77b5] | 65 | void *abac_xrealloc(void *ptr, size_t size) { |
---|
| 66 | void *ret = realloc(ptr, size); |
---|
| 67 | if (ret == NULL) |
---|
| 68 | err(1, "couldn't realloc %d bytes\n", size); |
---|
| 69 | return ret; |
---|
| 70 | } |
---|
| 71 | |
---|
| 72 | |
---|
[7f25a67f] | 73 | /** |
---|
| 74 | * strdup fatal on error |
---|
| 75 | */ |
---|
[3c251d0] | 76 | char *abac_xstrdup(char *source) { |
---|
[7f25a67f] | 77 | char *ret; |
---|
| 78 | |
---|
[7727f26] | 79 | if (source == NULL) { |
---|
[ff3d104] | 80 | return NULL; |
---|
[7727f26] | 81 | } |
---|
[ff3d104] | 82 | |
---|
[7f25a67f] | 83 | ret = strdup(source); |
---|
| 84 | if (ret == NULL) |
---|
| 85 | err(1, "strdup"); |
---|
| 86 | |
---|
| 87 | return ret; |
---|
| 88 | } |
---|
[9a411d7] | 89 | |
---|
[c586a3c] | 90 | /* make a brand new string with or without being trimmed */ |
---|
| 91 | char *abac_trim_quotes(char *string) { |
---|
| 92 | char *tmp=abac_xstrdup(string); |
---|
| 93 | int len=strlen(tmp); |
---|
| 94 | if( (tmp[len-1] == '\'' || tmp[len-1] == '"') && |
---|
| 95 | (tmp[0] == '\'' || tmp[0] == '"')) { |
---|
| 96 | tmp[len-1]='\0'; |
---|
| 97 | char *ntmp=abac_xstrdup(&tmp[1]); |
---|
| 98 | free(tmp); |
---|
| 99 | return ntmp; |
---|
| 100 | } |
---|
| 101 | return tmp; |
---|
| 102 | } |
---|
| 103 | |
---|
[9a411d7] | 104 | /** |
---|
| 105 | * Split a string based on the given delimiter. |
---|
| 106 | */ |
---|
| 107 | void abac_split(char *string, char *delim, char **ret, int *num) { |
---|
| 108 | int len = strlen(delim); |
---|
| 109 | char *start = string; |
---|
| 110 | int count = 0; |
---|
| 111 | |
---|
| 112 | // split the string by the delim |
---|
| 113 | while ((start = strstr(string, delim)) != NULL) { |
---|
| 114 | *start = 0; |
---|
| 115 | ret[count++] = string; |
---|
| 116 | string = start + len; |
---|
| 117 | } |
---|
| 118 | ret[count++] = string; |
---|
| 119 | |
---|
| 120 | *num = count; |
---|
| 121 | } |
---|
[8bd77b5] | 122 | |
---|
| 123 | int abac_validate_clean_name(char *string) { |
---|
| 124 | int i; |
---|
| 125 | |
---|
| 126 | assert(string != NULL); |
---|
| 127 | |
---|
| 128 | // must start with a letter/number |
---|
| 129 | if (!isalnum(string[0])) return 0; |
---|
| 130 | |
---|
| 131 | // Name must be alphanumeric or - |
---|
| 132 | for (i = 1; string[i] != '\0'; ++i) |
---|
| 133 | if (!isalnum(string[i]) && string[i] != '-') |
---|
| 134 | return 0; |
---|
| 135 | |
---|
| 136 | return 1; |
---|
| 137 | } |
---|
| 138 | |
---|
| 139 | /* check up to first ( if its there.. |
---|
| 140 | a.role(this) |
---|
| 141 | a.role(?,?Year:[100..200],?Value:[one,two,three]) |
---|
| 142 | a.role(isi) |
---|
| 143 | */ |
---|
| 144 | int abac_validate_clean_aspect_name(char *string) { |
---|
| 145 | int i; |
---|
| 146 | |
---|
| 147 | assert(string != NULL); |
---|
| 148 | |
---|
| 149 | // must start with a letter/number |
---|
| 150 | if (!isalnum(string[0])) return 0; |
---|
| 151 | |
---|
| 152 | // Name must be alphanumeric or - or _ up to the ( if there is one |
---|
| 153 | for (i = 1; string[i] != '\0' && string[i] != '(' ; ++i) |
---|
| 154 | if (!isalnum(string[i]) && string[i] != '-' && |
---|
| 155 | string[i] != '_' ) |
---|
| 156 | return 0; |
---|
| 157 | return 1; |
---|
| 158 | } |
---|
| 159 | |
---|
| 160 | chunk_t abac_generate_serial() { |
---|
| 161 | chunk_t serial = chunk_empty; |
---|
| 162 | |
---|
| 163 | // create a serial (stolen from strongswan pki) |
---|
| 164 | rng_t *rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); |
---|
| 165 | if (!rng) |
---|
| 166 | errx(1, "no random number generator"); |
---|
| 167 | |
---|
| 168 | rng->allocate_bytes(rng, 8, &serial); |
---|
[137b55f] | 169 | while (serial.ptr[0] == 0) { |
---|
[8bd77b5] | 170 | // don't get leading 0's |
---|
| 171 | rng->get_bytes(rng, 1, serial.ptr); |
---|
[137b55f] | 172 | } |
---|
[8bd77b5] | 173 | rng->destroy(rng); |
---|
| 174 | |
---|
[137b55f] | 175 | if(debug) |
---|
[b92a620] | 176 | fprintf(stderr,"abac_generate_serial, serial generated is %#B\n", &serial); |
---|
[8bd77b5] | 177 | return serial; |
---|
| 178 | } |
---|
| 179 | |
---|
| 180 | // validate a princpal's name |
---|
| 181 | // makes sure it's a valid SHA1 identifier |
---|
| 182 | // return values: |
---|
| 183 | // success: malloc'd copy with all hex digits lowercase |
---|
| 184 | // fail: NULL |
---|
| 185 | char *abac_validate_principal(char *keyid) { |
---|
| 186 | int i; |
---|
| 187 | char *copy = NULL; |
---|
| 188 | |
---|
| 189 | if (strlen(keyid) != SHA1_LENGTH) |
---|
| 190 | return NULL; |
---|
| 191 | |
---|
| 192 | int len = strlen(keyid) + 1; |
---|
| 193 | copy = abac_xmalloc(len); |
---|
| 194 | sprintf(copy, "%s", keyid); |
---|
| 195 | |
---|
| 196 | i=0; |
---|
| 197 | for (; i < SHA1_LENGTH; ++i) { |
---|
| 198 | copy[i] = tolower(copy[i]); |
---|
| 199 | if (!isxdigit(copy[i])) |
---|
| 200 | goto error; |
---|
| 201 | } |
---|
| 202 | return copy; |
---|
| 203 | error: |
---|
| 204 | free(copy); |
---|
| 205 | return NULL; |
---|
| 206 | } |
---|
| 207 | |
---|
| 208 | // parse&validate the validity |
---|
| 209 | int abac_validate_validity_string(char *validity_str) |
---|
| 210 | { |
---|
| 211 | int validity=0; |
---|
| 212 | if (validity_str != NULL) { |
---|
| 213 | char suffix = 'd'; // default suffix is days |
---|
| 214 | int multiplier; |
---|
| 215 | |
---|
| 216 | int len = strlen(validity_str); |
---|
| 217 | assert(len > 0); |
---|
| 218 | |
---|
| 219 | // get the suffix char if it's alphabetical |
---|
| 220 | if (isalpha(validity_str[len - 1])) { |
---|
| 221 | suffix = validity_str[len - 1]; |
---|
| 222 | |
---|
| 223 | // truncate |
---|
| 224 | validity_str[len - 1] = '\0'; |
---|
| 225 | --len; |
---|
| 226 | |
---|
| 227 | // make sure it's not only a suffix |
---|
| 228 | if (len == 0) { |
---|
| 229 | errx(1,"Invalid validity\n"); |
---|
| 230 | } |
---|
| 231 | } |
---|
| 232 | |
---|
| 233 | // convert the suffix to a multiplier |
---|
| 234 | switch(suffix) { |
---|
| 235 | case 's': multiplier = 1; break; |
---|
| 236 | case 'm': multiplier = 60; break; |
---|
| 237 | case 'h': multiplier = 3600; break; |
---|
| 238 | case 'd': multiplier = 86400; break; |
---|
| 239 | case 'y': multiplier = 31536000; break; |
---|
| 240 | default: |
---|
| 241 | errx(1,"Invalid suffix, must be s m h d y\n"); |
---|
| 242 | } |
---|
| 243 | |
---|
| 244 | // ascii to int |
---|
| 245 | char *end; |
---|
| 246 | validity = strtol(validity_str, &end, 10); |
---|
| 247 | if (end - validity_str < len) { |
---|
| 248 | errx(1,"Invalid validity\n"); |
---|
| 249 | } |
---|
| 250 | |
---|
| 251 | if (validity <= 0) { |
---|
| 252 | errx(1,"Invalid validity: must be > 0\n"); |
---|
| 253 | } |
---|
| 254 | |
---|
| 255 | // multiply! |
---|
| 256 | validity *= multiplier; |
---|
| 257 | } |
---|
| 258 | return validity; |
---|
| 259 | } |
---|
| 260 | |
---|
| 261 | |
---|
[2efdff5] | 262 | int file_exist(char *filename) |
---|
[8bd77b5] | 263 | { |
---|
[2efdff5] | 264 | struct stat stbuf; |
---|
| 265 | if(stat(filename,&stbuf) == -1) |
---|
| 266 | return 0; |
---|
| 267 | return 1; |
---|
[8bd77b5] | 268 | } |
---|
| 269 | |
---|
[2efdff5] | 270 | |
---|
[ba6027a] | 271 | chunk_t abac_get_potato_file(char *filename) |
---|
| 272 | { |
---|
| 273 | chunk_t pp=chunk_empty; |
---|
| 274 | if(file_exist(filename)) { |
---|
[b92a620] | 275 | if(debug) fprintf(stderr,"trying to open %s\n", filename); |
---|
[ba6027a] | 276 | FILE *fp=fopen(filename,"r"); |
---|
| 277 | char str[PWLEN]; |
---|
| 278 | int rc=fscanf(fp,"%s",str); |
---|
| 279 | if(rc) { |
---|
| 280 | pp.ptr=abac_xstrdup(str); |
---|
| 281 | pp.len=strlen(pp.ptr); |
---|
| 282 | if(str[pp.len-1]=='\n') |
---|
| 283 | pp.len--; |
---|
| 284 | } |
---|
| 285 | abac_clean_string(str); |
---|
| 286 | fclose(fp); |
---|
| 287 | } |
---|
| 288 | return pp; |
---|
| 289 | } |
---|
| 290 | |
---|
| 291 | /*** |
---|
| 292 | ***/ |
---|
| 293 | chunk_t abac_get_potato_cb(int try, char *name) { |
---|
| 294 | /* Get a password from stdin and return it as a chunk_t. If too many tries |
---|
| 295 | * have occurred or there is any other problem, return an empty chunk_t, |
---|
| 296 | * which libstrongswan takes as giving up. The chunk is alloated here |
---|
| 297 | * (inside getline), and presumably freed by libstrongswan. User points to |
---|
| 298 | * a cb_opts struct, which affects this routine in the obvious ways. |
---|
| 299 | */ |
---|
| 300 | /* Configuration options */ |
---|
| 301 | |
---|
| 302 | if(name!=NULL) { |
---|
| 303 | char *n=(char *)memrchr(name,'/',strlen(name)); |
---|
| 304 | if(n==NULL) |
---|
| 305 | printf("[%s] ", name); |
---|
| 306 | else printf("[%s] ",n); |
---|
| 307 | } |
---|
| 308 | struct cb_opts c_opts = { 1, 0, 3, "Key passphrase:" }; |
---|
| 309 | struct cb_opts *opts = &c_opts; |
---|
| 310 | chunk_t rv = chunk_empty; /* Return value, starts empty */ |
---|
| 311 | |
---|
| 312 | if (try -1 < opts->tries ) { |
---|
| 313 | struct termios t; /* Terminal settings */ |
---|
| 314 | size_t len = 0; /* Length of string from getline */ |
---|
| 315 | tcflag_t orig = 0; /* Holds the original local flags (echo in here) */ |
---|
| 316 | |
---|
| 317 | if (!opts->use_echo) { |
---|
| 318 | /* Use tc{get,set}attr to turn echo off and restore the intial |
---|
| 319 | * echo settings */ |
---|
| 320 | if (!tcgetattr(0, &t)) { |
---|
| 321 | orig = t.c_lflag; |
---|
| 322 | |
---|
| 323 | t.c_lflag &= ~ECHO; |
---|
| 324 | if ( tcsetattr(0, TCSANOW, &t) ) { |
---|
| 325 | perror("Cannot turn off echo"); |
---|
| 326 | return rv; |
---|
| 327 | } |
---|
| 328 | } |
---|
| 329 | else { |
---|
| 330 | perror("Cannot turn get attributes to off echo"); |
---|
| 331 | return rv; |
---|
| 332 | } |
---|
| 333 | } |
---|
| 334 | if (opts->use_prompt) printf("%s", opts->prompt); |
---|
| 335 | |
---|
| 336 | /* Because rv.ptr starts as NULL, getline allocates memory. The size |
---|
| 337 | * of the allocation returns in rv.len and the size of the string |
---|
| 338 | * (including newline and NUL) is in len. */ |
---|
| 339 | if ((rv.ptr = (u_char *) malloc(rv.len = PWLEN))) { |
---|
| 340 | if ( fgets(rv.ptr, rv.len, stdin) ) { |
---|
| 341 | /* Readjust the chunk_t's len field to the size of the string |
---|
| 342 | * w/o the newline or NUL */ |
---|
| 343 | /* would prefer strnlen, but no such luck in FBSD7 or earlier*/ |
---|
| 344 | size_t len = strlen(rv.ptr); |
---|
| 345 | |
---|
| 346 | /* \n\0, \n or \0 */ |
---|
| 347 | if (rv.ptr[len-2] == '\n') rv.len = len-2; |
---|
| 348 | else if (rv.ptr[len-1] == '\n') rv.len = len-1; |
---|
| 349 | else rv.len = len -1; |
---|
| 350 | } else { |
---|
| 351 | /* Read failed. Deallocate and clear rv */ |
---|
| 352 | free(rv.ptr); |
---|
| 353 | rv = chunk_empty; |
---|
| 354 | } |
---|
| 355 | } |
---|
| 356 | else { |
---|
| 357 | /* Failed malloc. Restore rv to empty and return it */ |
---|
| 358 | perror("malloc"); |
---|
| 359 | rv = chunk_empty; |
---|
| 360 | return rv; |
---|
| 361 | } |
---|
| 362 | |
---|
| 363 | if (!opts->use_echo ) { |
---|
| 364 | /* Pop echo beck to its original setting. */ |
---|
| 365 | t.c_lflag = orig; |
---|
| 366 | |
---|
| 367 | if ( tcsetattr(0, TCSANOW, &t) ) |
---|
| 368 | perror("Cannot restore echo setting?"); |
---|
| 369 | |
---|
| 370 | if (opts->use_prompt) printf("\n"); |
---|
| 371 | } |
---|
| 372 | } |
---|
| 373 | else fprintf(stderr, "Too many tries (%d)", try-1); |
---|
| 374 | return rv; |
---|
| 375 | } |
---|
| 376 | |
---|
| 377 | chunk_t extract_potato(char *filename, char *pfile) |
---|
| 378 | { |
---|
| 379 | chunk_t pp; |
---|
| 380 | if(pfile==NULL || strlen(pfile)==0 ) { |
---|
| 381 | pp=abac_get_potato_cb(2,filename); |
---|
| 382 | } else { |
---|
| 383 | pp=abac_get_potato_file(pfile); |
---|
| 384 | } |
---|
| 385 | return pp; |
---|
| 386 | } |
---|