Whamcloud - gitweb
LU-6210 utils: Change positional struct initializers to C99
[fs/lustre-release.git] / lustre / utils / gss / lgss_sk.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 only,
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License version 2 for more details (a copy is included
14  * in the LICENSE file that accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License
17  * version 2 along with this program; If not, see
18  * http://www.gnu.org/licenses/gpl-2.0.html
19  *
20  * GPL HEADER END
21  */
22 /*
23  * Copyright (C) 2015, Trustees of Indiana University
24  *
25  * Copyright (c) 2016, Intel Corporation.
26  *
27  * Author: Jeremy Filizetti <jfilizet@iu.edu>
28  */
29
30 #include <ctype.h>
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <getopt.h>
34 #include <limits.h>
35 #include <stdarg.h>
36 #include <stdbool.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <stdlib.h>
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <unistd.h>
43 #include <lnet/nidstr.h>
44 #include <lustre/lustre_idl.h>
45
46 #include "sk_utils.h"
47 #include "err_util.h"
48
49 #ifndef _GNU_SOURCE
50 #define _GNU_SOURCE
51 #endif
52
53 /* One week default expiration */
54 #define SK_DEFAULT_EXPIRE 604800
55 #define SK_DEFAULT_SK_KEYLEN 256
56 #define SK_DEFAULT_PRIME_BITS 2048
57 #define SK_DEFAULT_NODEMAP "default"
58
59 /* Names match up with openssl enc and dgst commands */
60 char *sk_crypt2name[] = {
61         [SK_CRYPT_EMPTY] = "NONE",
62         [SK_CRYPT_AES256_CTR] = "AES-256-CTR",
63 };
64
65 const char *sk_hmac2name[] = { "NONE", "SHA256", "SHA512" };
66
67 static int sk_name2crypt(char *name)
68 {
69         int i;
70
71         for (i = 0; i < SK_CRYPT_MAX; i++) {
72                 if (strcasecmp(name, sk_crypt2name[i]) == 0)
73                         return i;
74         }
75
76         return SK_CRYPT_INVALID;
77 }
78
79 enum cfs_crypto_hash_alg sk_name2hmac(char *name)
80 {
81         enum cfs_crypto_hash_alg algo;
82         int i = 0;
83
84         /* convert to lower case */
85         while (name[i]) {
86                 putchar(tolower(name[i]));
87                 i++;
88         }
89
90         if (strcmp(name, "none"))
91                 return CFS_HASH_ALG_NULL;
92
93         algo = cfs_crypto_hash_alg(name);
94         if ((algo != CFS_HASH_ALG_SHA256) ||
95             (algo != CFS_HASH_ALG_SHA512))
96                 return SK_HMAC_INVALID;
97
98         return algo;
99 }
100
101 static void usage(FILE *fp, char *program)
102 {
103         int i;
104
105         fprintf(fp, "Usage %s [OPTIONS] {-l|-m|-r|-w} <keyfile>\n", program);
106         fprintf(fp, "-l|--load       <keyfile>  Load key from file into user's "
107                 "session keyring\n");
108         fprintf(fp, "-m|--modify     <keyfile>  Modify keyfile's attributes\n");
109         fprintf(fp, "-r|--read       <keyfile>  Show keyfile's attributes\n");
110         fprintf(fp, "-w|--write      <keyfile>  Generate keyfile\n\n");
111         fprintf(fp, "Modify/Write Options:\n");
112         fprintf(fp, "-c|--crypt      <num>      Cipher for encryption "
113                 "(Default: AES Counter mode)\n");
114         for (i = 1; i < SK_CRYPT_MAX; i++)
115                 fprintf(fp, "                        %s\n", sk_crypt2name[i]);
116
117         fprintf(fp, "-i|--hmac       <num>      Hash algorithm for integrity "
118                 "(Default: SHA256)\n");
119         for (i = 1; i < sizeof(sk_hmac2name) / sizeof(sk_hmac2name[0]); i++)
120                 fprintf(fp, "                        %s\n", sk_hmac2name[i]);
121
122         fprintf(fp, "-e|--expire     <num>      Seconds before contexts from "
123                 "key expire (Default: %d seconds (%.3g days))\n",
124                 SK_DEFAULT_EXPIRE, (double)SK_DEFAULT_EXPIRE / 3600 / 24);
125         fprintf(fp, "-f|--fsname     <name>     File system name for key\n");
126         fprintf(fp, "-g|--mgsnids    <nids>     Comma seperated list of MGS "
127                 "NIDs.  Only required when mgssec is used (Default: \"\")\n");
128         fprintf(fp, "-n|--nodemap    <name>     Nodemap name for key "
129                 "(Default: \"%s\")\n", SK_DEFAULT_NODEMAP);
130         fprintf(fp, "-p|--prime-bits <len>      Prime length (p) for DHKE in "
131                 "bits (Default: %d)\n", SK_DEFAULT_PRIME_BITS);
132         fprintf(fp, "-t|--type       <type>     Key type (mgs, server, "
133                 "client)\n");
134         fprintf(fp, "-k|--key-bits   <len>      Shared key length in bits "
135                 "(Default: %d)\n", SK_DEFAULT_SK_KEYLEN);
136         fprintf(fp, "-d|--data       <file>     Key random data source "
137                 "(Default: /dev/random)\n\n");
138         fprintf(fp, "Other Options:\n");
139         fprintf(fp, "-v|--verbose           Increase verbosity for errors\n");
140         exit(EXIT_FAILURE);
141 }
142
143 static ssize_t get_key_data(char *src, void *buffer, size_t bits)
144 {
145         char *ptr = buffer;
146         size_t remain;
147         ssize_t rc;
148         int fd;
149
150         /* convert bits to minimum number of bytes */
151         remain = (bits + 7) / 8;
152
153         printf("Reading random data for shared key from '%s'\n", src);
154         fd = open(src, O_RDONLY);
155         if (fd < 0) {
156                 fprintf(stderr, "error: opening '%s': %s\n", src,
157                         strerror(errno));
158                 return -errno;
159         }
160
161         while (remain > 0) {
162                 rc = read(fd, ptr, remain);
163                 if (rc < 0) {
164                         if (errno == EINTR)
165                                 continue;
166                         fprintf(stderr, "error: reading from '%s': %s\n", src,
167                                 strerror(errno));
168                         rc = -errno;
169                         goto out;
170
171                 } else if (rc == 0) {
172                         fprintf(stderr,
173                                 "error: key source too short for %zd-bit key\n",
174                                 bits);
175                         rc = -ENODATA;
176                         goto out;
177                 }
178                 ptr += rc;
179                 remain -= rc;
180         }
181         rc = 0;
182
183 out:
184         close(fd);
185         return rc;
186 }
187
188 static int write_config_file(char *output_file,
189                              struct sk_keyfile_config *config, bool overwrite)
190 {
191         size_t rc;
192         int fd;
193         int flags = O_WRONLY | O_CREAT;
194
195         if (!overwrite)
196                 flags |= O_EXCL;
197
198         sk_config_cpu_to_disk(config);
199
200         fd = open(output_file, flags, 0400);
201         if (fd < 0) {
202                 fprintf(stderr, "error: opening '%s': %s\n", output_file,
203                         strerror(errno));
204                 return -errno;
205         }
206
207         rc = write(fd, config, sizeof(*config));
208         if (rc < 0) {
209                 fprintf(stderr, "error: writing to '%s': %s\n", output_file,
210                         strerror(errno));
211                 rc = -errno;
212         } else if (rc != sizeof(*config)) {
213                 fprintf(stderr, "error: short write to '%s'\n", output_file);
214                 rc = -ENOSPC;
215
216         } else {
217                 rc = 0;
218         }
219
220         close(fd);
221         return rc;
222 }
223
224 static int print_config(char *filename)
225 {
226         struct sk_keyfile_config *config;
227         int i;
228
229         config = sk_read_file(filename);
230         if (!config)
231                 return EXIT_FAILURE;
232
233         if (sk_validate_config(config)) {
234                 fprintf(stderr, "error: key configuration failed validation\n");
235                 free(config);
236                 return EXIT_FAILURE;
237         }
238
239         printf("Version:        %u\n", config->skc_version);
240         printf("Type:          ");
241         if (config->skc_type & SK_TYPE_MGS)
242                 printf(" mgs");
243         if (config->skc_type & SK_TYPE_SERVER)
244                 printf(" server");
245         if (config->skc_type & SK_TYPE_CLIENT)
246                 printf(" client");
247         printf("\n");
248         printf("HMAC alg:       %s\n", sk_hmac2name[config->skc_hmac_alg]);
249         printf("Crypto alg:     %s\n", cfs_crypto_hash_name(config->skc_hmac_alg));
250         printf("Ctx Expiration: %u seconds\n", config->skc_expire);
251         printf("Shared keylen:  %u bits\n", config->skc_shared_keylen);
252         printf("Prime length:   %u bits\n", config->skc_prime_bits);
253         printf("File system:    %s\n", config->skc_fsname);
254         printf("MGS NIDs:      ");
255         for (i = 0; i < MAX_MGSNIDS; i++) {
256                 if (config->skc_mgsnids[i] == LNET_NID_ANY)
257                         continue;
258                 printf(" %s", libcfs_nid2str(config->skc_mgsnids[i]));
259         }
260         printf("\n");
261         printf("Nodemap name:   %s\n", config->skc_nodemap);
262         printf("Shared key:\n");
263         print_hex(0, config->skc_shared_key, config->skc_shared_keylen / 8);
264
265         /* Don't print empty keys */
266         for (i = 0; i < SK_MAX_P_BYTES; i++)
267                 if (config->skc_p[i] != 0)
268                         break;
269
270         if (i != SK_MAX_P_BYTES) {
271                 printf("Prime (p):\n");
272                 print_hex(0, config->skc_p, config->skc_prime_bits / 8);
273         }
274
275         free(config);
276         return EXIT_SUCCESS;
277 }
278
279 static int parse_mgsnids(char *mgsnids, struct sk_keyfile_config *config)
280 {
281         lnet_nid_t nid;
282         char *ptr;
283         char *sep;
284         char *end;
285         int rc = 0;
286         int i;
287
288         /* replace all old values */
289         for (i = 0; i < MAX_MGSNIDS; i++)
290                 config->skc_mgsnids[i] = LNET_NID_ANY;
291
292         i = 0;
293         end = mgsnids + strlen(mgsnids);
294         ptr = mgsnids;
295         while (ptr < end && i < MAX_MGSNIDS) {
296                 sep = strstr(ptr, ",");
297                 if (sep != NULL)
298                         *sep = '\0';
299
300                 nid = libcfs_str2nid(ptr);
301                 if (nid == LNET_NID_ANY) {
302                         fprintf(stderr, "error: invalid MGS NID: %s\n", ptr);
303                         rc = -EINVAL;
304                         break;
305                 }
306
307                 config->skc_mgsnids[i++] = nid;
308                 ptr += strlen(ptr) + 1;
309         }
310
311         if (i == MAX_MGSNIDS) {
312                 fprintf(stderr, "error: more than %u MGS NIDs provided\n", i);
313                 rc = -E2BIG;
314         }
315
316         return rc;
317 }
318
319 int main(int argc, char **argv)
320 {
321         struct sk_keyfile_config *config;
322         char *datafile = NULL;
323         char *input = NULL;
324         char *load = NULL;
325         char *modify = NULL;
326         char *output = NULL;
327         char *mgsnids = NULL;
328         char *nodemap = NULL;
329         char *fsname = NULL;
330         char *tmp;
331         char *tmp2;
332         int crypt = SK_CRYPT_EMPTY;
333         enum cfs_crypto_hash_alg hmac = CFS_HASH_ALG_NULL;
334         int expire = -1;
335         int shared_keylen = -1;
336         int prime_bits = -1;
337         int verbose = 0;
338         int i;
339         int opt;
340         enum sk_key_type  type = SK_TYPE_INVALID;
341         bool generate_prime = false;
342         DH *dh;
343
344         static struct option long_opts[] = {
345         { .name = "crypt",      .has_arg = required_argument, .val = 'c'},
346         { .name = "data",       .has_arg = required_argument, .val = 'd'},
347         { .name = "expire",     .has_arg = required_argument, .val = 'e'},
348         { .name = "fsname",     .has_arg = required_argument, .val = 'f'},
349         { .name = "mgsnids",    .has_arg = required_argument, .val = 'g'},
350         { .name = "help",       .has_arg = no_argument,       .val = 'h'},
351         { .name = "hmac",       .has_arg = required_argument, .val = 'i'},
352         { .name = "integrity",  .has_arg = required_argument, .val = 'i'},
353         { .name = "key-bits",   .has_arg = required_argument, .val = 'k'},
354         { .name = "shared",     .has_arg = required_argument, .val = 'k'},
355         { .name = "load",       .has_arg = required_argument, .val = 'l'},
356         { .name = "modify",     .has_arg = required_argument, .val = 'm'},
357         { .name = "nodemap",    .has_arg = required_argument, .val = 'n'},
358         { .name = "prime-bits", .has_arg = required_argument, .val = 'p'},
359         { .name = "read",       .has_arg = required_argument, .val = 'r'},
360         { .name = "type",       .has_arg = required_argument, .val = 't'},
361         { .name = "verbose",    .has_arg = no_argument,       .val = 'v'},
362         { .name = "write",      .has_arg = required_argument, .val = 'w'},
363         { .name = NULL, } };
364
365         while ((opt = getopt_long(argc, argv,
366                                   "c:d:e:f:g:hi:l:m:n:p:r:s:k:t:w:v", long_opts,
367                                   NULL)) != EOF) {
368                 switch (opt) {
369                 case 'c':
370                         crypt = sk_name2crypt(optarg);
371                         break;
372                 case 'd':
373                         datafile = optarg;
374                         break;
375                 case 'e':
376                         expire = atoi(optarg);
377                         if (expire < 60)
378                                 fprintf(stderr, "warning: using a %us key "
379                                         "expiration may cause issues during "
380                                         "key renegotiation\n", expire);
381                         break;
382                 case 'f':
383                         fsname = optarg;
384                         if (strlen(fsname) > MTI_NAME_MAXLEN) {
385                                 fprintf(stderr,
386                                         "error: file system name longer than "
387                                         "%u characters\n", MTI_NAME_MAXLEN);
388                                 return EXIT_FAILURE;
389                         }
390                         break;
391                 case 'g':
392                         mgsnids = optarg;
393                         break;
394                 case 'h':
395                         usage(stdout, argv[0]);
396                         break;
397                 case 'i':
398                         hmac = sk_name2hmac(optarg);
399                         break;
400                 case 'k':
401                         shared_keylen = atoi(optarg);
402                         break;
403                 case 'l':
404                         load = optarg;
405                         break;
406                 case 'm':
407                         modify = optarg;
408                         break;
409                 case 'n':
410                         nodemap = optarg;
411                         if (strlen(nodemap) > LUSTRE_NODEMAP_NAME_LENGTH) {
412                                 fprintf(stderr,
413                                         "error: nodemap name longer than "
414                                         "%u characters\n",
415                                         LUSTRE_NODEMAP_NAME_LENGTH);
416                                 return EXIT_FAILURE;
417                         }
418                         break;
419                 case 'p':
420                         prime_bits = atoi(optarg);
421                         if (prime_bits <= 0) {
422                                 fprintf(stderr,
423                                         "error: invalid prime length: '%s'\n",
424                                         optarg);
425                                 return EXIT_FAILURE;
426                         }
427                         break;
428                 case 'r':
429                         input = optarg;
430                         break;
431                 case 't':
432                         tmp2 = strdup(optarg);
433                         if (!tmp2) {
434                                 fprintf(stderr,
435                                         "error: failed to allocate type\n");
436                                 return EXIT_FAILURE;
437                         }
438                         tmp = strsep(&tmp2, ",");
439                         while (tmp != NULL) {
440                                 if (strcasecmp(tmp, "server") == 0) {
441                                         type |= SK_TYPE_SERVER;
442                                 } else if (strcasecmp(tmp, "mgs") == 0) {
443                                         type |= SK_TYPE_MGS;
444                                 } else if (strcasecmp(tmp, "client") == 0) {
445                                         type |= SK_TYPE_CLIENT;
446                                 } else {
447                                         fprintf(stderr,
448                                                 "error: invalid type '%s', "
449                                                 "must be mgs, server, or client"
450                                                 "\n", optarg);
451                                         return EXIT_FAILURE;
452                                 }
453                                 tmp = strsep(&tmp2, ",");
454                         }
455                         free(tmp2);
456                         break;
457                 case 'v':
458                         verbose++;
459                         break;
460                 case 'w':
461                         output = optarg;
462                         break;
463                 default:
464                         fprintf(stderr, "error: unknown option: '%c'\n", opt);
465                         return EXIT_FAILURE;
466                         break;
467                 }
468         }
469
470         if (optind != argc) {
471                 fprintf(stderr,
472                         "error: extraneous arguments provided, check usage\n");
473                 return EXIT_FAILURE;
474         }
475
476         if (!input && !output && !load && !modify) {
477                 usage(stderr, argv[0]);
478                 return EXIT_FAILURE;
479         }
480
481         /* init gss logger for foreground (no syslog) which prints to stderr */
482         initerr(NULL, verbose, 1);
483
484         if (input)
485                 return print_config(input);
486
487         if (load) {
488                 if (sk_load_keyfile(load))
489                         return EXIT_FAILURE;
490                 return EXIT_SUCCESS;
491         }
492
493         if (crypt == SK_CRYPT_INVALID) {
494                 fprintf(stderr, "error: invalid crypt algorithm specified\n");
495                 return EXIT_FAILURE;
496         }
497         if (hmac == SK_HMAC_INVALID) {
498                 fprintf(stderr, "error: invalid HMAC algorithm specified\n");
499                 return EXIT_FAILURE;
500         }
501
502         if (modify) {
503                 config = sk_read_file(modify);
504                 if (!config)
505                         return EXIT_FAILURE;
506
507                 if (type != SK_TYPE_INVALID) {
508                         /* generate key when adding client type */
509                         if (!(config->skc_type & SK_TYPE_CLIENT) &&
510                             type & SK_TYPE_CLIENT)
511                                 generate_prime = true;
512                         else if (!(type & SK_TYPE_CLIENT))
513                                 memset(config->skc_p, 0, SK_MAX_P_BYTES);
514
515                         config->skc_type = type;
516                 }
517                 if (prime_bits != -1) {
518                         memset(config->skc_p, 0, SK_MAX_P_BYTES);
519                         if (config->skc_prime_bits != prime_bits &&
520                             config->skc_type & SK_TYPE_CLIENT)
521                                 generate_prime = true;
522                 }
523         } else {
524                 /* write mode for a new key */
525                 if (!fsname && !mgsnids) {
526                         fprintf(stderr,
527                                 "error: missing --fsname or --mgsnids\n");
528                         return EXIT_FAILURE;
529                 }
530
531                 config = calloc(1, sizeof(*config));
532                 if (!config)
533                         return EXIT_FAILURE;
534
535                 /* Set the defaults for new key */
536                 config->skc_version = SK_CONF_VERSION;
537                 config->skc_expire = SK_DEFAULT_EXPIRE;
538                 config->skc_shared_keylen = SK_DEFAULT_SK_KEYLEN;
539                 config->skc_prime_bits = SK_DEFAULT_PRIME_BITS;
540                 config->skc_crypt_alg = SK_CRYPT_AES256_CTR;
541                 config->skc_hmac_alg = CFS_HASH_ALG_SHA256;
542                 for (i = 0; i < MAX_MGSNIDS; i++)
543                         config->skc_mgsnids[i] = LNET_NID_ANY;
544
545                 if (type == SK_TYPE_INVALID) {
546                         fprintf(stderr, "error: no type specified for key\n");
547                         goto error;
548                 }
549                 config->skc_type = type;
550                 generate_prime = type & SK_TYPE_CLIENT;
551
552                 strncpy(config->skc_nodemap, SK_DEFAULT_NODEMAP,
553                         strlen(SK_DEFAULT_NODEMAP));
554
555                 if (!datafile)
556                         datafile = "/dev/random";
557         }
558
559         if (crypt != SK_CRYPT_EMPTY)
560                 config->skc_crypt_alg = crypt;
561         if (hmac != CFS_HASH_ALG_NULL)
562                 config->skc_hmac_alg = hmac;
563         if (expire != -1)
564                 config->skc_expire = expire;
565         if (shared_keylen != -1)
566                 config->skc_shared_keylen = shared_keylen;
567         if (prime_bits != -1)
568                 config->skc_prime_bits = prime_bits;
569         if (fsname)
570                 strncpy(config->skc_fsname, fsname, strlen(fsname));
571         if (nodemap)
572                 strncpy(config->skc_nodemap, nodemap, strlen(nodemap));
573         if (mgsnids && parse_mgsnids(mgsnids, config))
574                 goto error;
575         if (sk_validate_config(config)) {
576                 fprintf(stderr, "error: key configuration failed validation\n");
577                 goto error;
578         }
579
580         if (datafile && get_key_data(datafile, config->skc_shared_key,
581                                      config->skc_shared_keylen)) {
582                 fprintf(stderr, "error: failure getting key data from '%s'\n",
583                         datafile);
584                 goto error;
585         }
586
587         if (generate_prime) {
588                 printf("Generating DH parameters, this can take a while...\n");
589                 dh = DH_generate_parameters(config->skc_prime_bits,
590                                             SK_GENERATOR, NULL, NULL);
591                 if (BN_num_bytes(dh->p) > SK_MAX_P_BYTES) {
592                         fprintf(stderr, "error: cannot generate DH parameters: "
593                                 "requested length %d exceeds maximum %d\n",
594                                 config->skc_prime_bits, SK_MAX_P_BYTES * 8);
595                         goto error;
596                 }
597                 if (BN_bn2bin(dh->p, config->skc_p) != BN_num_bytes(dh->p)) {
598                         fprintf(stderr,
599                                 "error: convert BIGNUM p to binary failed\n");
600                         goto error;
601                 }
602         }
603
604         if (write_config_file(modify ?: output, config, modify))
605                 goto error;
606
607         return EXIT_SUCCESS;
608
609 error:
610         free(config);
611         return EXIT_FAILURE;
612 }