Whamcloud - gitweb
LU-15896 gss: support OpenSSLv3
[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 <linux/lustre/lustre_user.h>
44
45 #include "sk_utils.h"
46 #include "err_util.h"
47
48 #ifndef _GNU_SOURCE
49 #define _GNU_SOURCE
50 #endif
51
52 /* One week default expiration */
53 #define SK_DEFAULT_EXPIRE 604800
54 #define SK_DEFAULT_SK_KEYLEN 256
55 #define SK_DEFAULT_PRIME_BITS 2048
56 #define SK_DEFAULT_NODEMAP "default"
57
58 static void usage(FILE *fp, char *program)
59 {
60         int i;
61
62         fprintf(fp, "Usage %s [OPTIONS] {-l|-m|-r|-w} <keyfile>\n", program);
63         fprintf(fp, "-l|--load       <keyfile>  Load key from file into user's "
64                 "session keyring\n");
65         fprintf(fp, "-m|--modify     <keyfile>  Modify keyfile's attributes\n");
66         fprintf(fp, "-r|--read       <keyfile>  Show keyfile's attributes\n");
67         fprintf(fp, "-w|--write      <keyfile>  Generate keyfile\n\n");
68         fprintf(fp, "Modify/Write Options:\n");
69         fprintf(fp, "-c|--crypt      <num>      Cipher for encryption "
70                 "(Default: AES Counter mode)\n");
71         for (i = 1; i < ARRAY_SIZE(sk_crypt_algs); i++)
72                 fprintf(fp, "                        %s\n",
73                         sk_crypt_algs[i].sct_name);
74         fprintf(fp, "-i|--hmac       <num>      Hash algorithm for integrity "
75                 "(Default: SHA256)\n");
76         for (i = 1; i < ARRAY_SIZE(sk_hmac_algs); i++)
77                 fprintf(fp, "                        %s\n",
78                         sk_hmac_algs[i].sht_name);
79         fprintf(fp, "-e|--expire     <num>      Seconds before contexts from "
80                 "key expire (Default: %d seconds (%.3g days))\n",
81                 SK_DEFAULT_EXPIRE, (double)SK_DEFAULT_EXPIRE / 3600 / 24);
82         fprintf(fp, "-f|--fsname     <name>     File system name for key\n");
83         fprintf(fp, "-g|--mgsnids    <nids>     Comma seperated list of MGS "
84                 "NIDs.  Only required when mgssec is used (Default: \"\")\n");
85         fprintf(fp, "-n|--nodemap    <name>     Nodemap name for key "
86                 "(Default: \"%s\")\n", SK_DEFAULT_NODEMAP);
87         fprintf(fp, "-p|--prime-bits <len>      Prime length (p) for DHKE in "
88                 "bits (Default: %d)\n", SK_DEFAULT_PRIME_BITS);
89         fprintf(fp, "-t|--type       <type>     Key type (mgs, server, "
90                 "client)\n");
91         fprintf(fp, "-k|--key-bits   <len>      Shared key length in bits "
92                 "(Default: %d)\n", SK_DEFAULT_SK_KEYLEN);
93         fprintf(fp, "-d|--data       <file>     Key data source for new keys "
94                 "(Default: /dev/random)\n");
95         fprintf(fp, "                        Not a seed value. "
96                 "This is the actual key value.\n\n");
97         fprintf(fp, "Other Options:\n");
98         fprintf(fp, "-v|--verbose           Increase verbosity for errors\n");
99         exit(EXIT_FAILURE);
100 }
101
102 static ssize_t get_key_data(char *src, void *buffer, size_t bits)
103 {
104         char *ptr = buffer;
105         size_t remain;
106         ssize_t rc;
107         int fd;
108
109         /* convert bits to minimum number of bytes */
110         remain = (bits + 7) / 8;
111
112         printf("Reading random data for shared key from '%s'\n", src);
113         fd = open(src, O_RDONLY);
114         if (fd < 0) {
115                 fprintf(stderr, "error: opening '%s': %s\n", src,
116                         strerror(errno));
117                 return -errno;
118         }
119
120         while (remain > 0) {
121                 rc = read(fd, ptr, remain);
122                 if (rc < 0) {
123                         if (errno == EINTR)
124                                 continue;
125                         fprintf(stderr, "error: reading from '%s': %s\n", src,
126                                 strerror(errno));
127                         rc = -errno;
128                         goto out;
129
130                 } else if (rc == 0) {
131                         fprintf(stderr,
132                                 "error: key source too short for %zd-bit key\n",
133                                 bits);
134                         rc = -ENODATA;
135                         goto out;
136                 }
137                 ptr += rc;
138                 remain -= rc;
139         }
140         rc = 0;
141
142 out:
143         close(fd);
144         return rc;
145 }
146
147 static int write_config_file(char *output_file,
148                              struct sk_keyfile_config *config, bool overwrite)
149 {
150         size_t rc;
151         int fd;
152         int flags = O_WRONLY | O_CREAT;
153
154         if (!overwrite)
155                 flags |= O_EXCL;
156
157         sk_config_cpu_to_disk(config);
158
159         fd = open(output_file, flags, 0400);
160         if (fd < 0) {
161                 fprintf(stderr, "error: opening '%s': %s\n", output_file,
162                         strerror(errno));
163                 return -errno;
164         }
165
166         rc = write(fd, config, sizeof(*config));
167         if (rc < 0) {
168                 fprintf(stderr, "error: writing to '%s': %s\n", output_file,
169                         strerror(errno));
170                 rc = -errno;
171         } else if (rc != sizeof(*config)) {
172                 fprintf(stderr, "error: short write to '%s'\n", output_file);
173                 rc = -ENOSPC;
174
175         } else {
176                 rc = 0;
177         }
178
179         close(fd);
180         return rc;
181 }
182
183 static int print_config(char *filename)
184 {
185         struct sk_keyfile_config *config;
186         int i;
187
188         config = sk_read_file(filename);
189         if (!config)
190                 return EXIT_FAILURE;
191
192         if (sk_validate_config(config)) {
193                 fprintf(stderr, "error: key configuration failed validation\n");
194                 free(config);
195                 return EXIT_FAILURE;
196         }
197
198         printf("Version:        %u\n", config->skc_version);
199         printf("Type:          ");
200         if (config->skc_type & SK_TYPE_MGS)
201                 printf(" mgs");
202         if (config->skc_type & SK_TYPE_SERVER)
203                 printf(" server");
204         if (config->skc_type & SK_TYPE_CLIENT)
205                 printf(" client");
206         printf("\n");
207         printf("HMAC alg:       %s\n", sk_hmac2name(config->skc_hmac_alg));
208         printf("Crypto alg:     %s\n", sk_crypt2name(config->skc_crypt_alg));
209         printf("Ctx Expiration: %u seconds\n", config->skc_expire);
210         printf("Shared keylen:  %u bits\n", config->skc_shared_keylen);
211         printf("Prime length:   %u bits\n", config->skc_prime_bits);
212         printf("File system:    %s\n", config->skc_fsname);
213         printf("MGS NIDs:      ");
214         for (i = 0; i < MAX_MGSNIDS; i++) {
215                 if (config->skc_mgsnids[i] == LNET_NID_ANY)
216                         continue;
217                 printf(" %s", libcfs_nid2str(config->skc_mgsnids[i]));
218         }
219         printf("\n");
220         printf("Nodemap name:   %s\n", config->skc_nodemap);
221         printf("Shared key:\n");
222         print_hex(0, config->skc_shared_key, config->skc_shared_keylen / 8);
223
224         /* Don't print empty keys */
225         for (i = 0; i < SK_MAX_P_BYTES; i++)
226                 if (config->skc_p[i] != 0)
227                         break;
228
229         if (i != SK_MAX_P_BYTES) {
230                 printf("Prime (p):\n");
231                 print_hex(0, config->skc_p, config->skc_prime_bits / 8);
232         }
233
234         free(config);
235         return EXIT_SUCCESS;
236 }
237
238 static int parse_mgsnids(char *mgsnids, struct sk_keyfile_config *config)
239 {
240         lnet_nid_t nid;
241         char *ptr;
242         char *sep;
243         char *end;
244         int rc = 0;
245         int i;
246
247         /* replace all old values */
248         for (i = 0; i < MAX_MGSNIDS; i++)
249                 config->skc_mgsnids[i] = LNET_NID_ANY;
250
251         i = 0;
252         end = mgsnids + strlen(mgsnids);
253         ptr = mgsnids;
254         while (ptr < end && i < MAX_MGSNIDS) {
255                 sep = strstr(ptr, ",");
256                 if (sep != NULL)
257                         *sep = '\0';
258
259                 nid = libcfs_str2nid(ptr);
260                 if (nid == LNET_NID_ANY) {
261                         fprintf(stderr, "error: invalid MGS NID: %s\n", ptr);
262                         rc = -EINVAL;
263                         break;
264                 }
265
266                 config->skc_mgsnids[i++] = nid;
267                 ptr += strlen(ptr) + 1;
268         }
269
270         if (i == MAX_MGSNIDS) {
271                 fprintf(stderr, "error: more than %u MGS NIDs provided\n", i);
272                 rc = -E2BIG;
273         }
274
275         return rc;
276 }
277
278 static inline int __gen_ssk_prime(struct sk_keyfile_config *config)
279 {
280         int rc = -1;
281         EVP_PKEY_CTX *ctx = NULL;
282         EVP_PKEY *dh = NULL;
283         BIGNUM *p;
284
285         ctx = EVP_PKEY_CTX_new_from_name(NULL, "DH", NULL);
286         if (!ctx || EVP_PKEY_paramgen_init(ctx) != 1) {
287                 fprintf(stderr, "error: ctx cannot be init\n");
288                 goto prime_end;
289         }
290
291         if (EVP_PKEY_CTX_set_dh_paramgen_prime_len(ctx,
292                                                  config->skc_prime_bits) <= 0 ||
293             EVP_PKEY_CTX_set_dh_paramgen_generator(ctx, SK_GENERATOR) <= 0) {
294                 fprintf(stderr, "error: cannot set prime or generator\n");
295                 goto prime_end;
296         }
297
298         if (EVP_PKEY_paramgen(ctx, &dh) != 1) {
299                 fprintf(stderr, "error: cannot generate DH parameters\n");
300                 goto prime_end;
301         }
302
303         if (!EVP_PKEY_get_bn_param(dh, OSSL_PKEY_PARAM_FFC_P, &p)) {
304                 fprintf(stderr, "error: cannot get p from dh\n");
305                 goto prime_end;
306         }
307
308         if (BN_num_bytes(p) > SK_MAX_P_BYTES) {
309                 fprintf(stderr,
310                         "error: cannot generate DH parameters: requested length %d exceeds maximum %d\n",
311                         config->skc_prime_bits, SK_MAX_P_BYTES * 8);
312                 goto prime_end;
313         }
314         if (BN_bn2bin(p, config->skc_p) != BN_num_bytes(p)) {
315                 fprintf(stderr,
316                         "error: convert BIGNUM p to binary failed\n");
317                 goto prime_end;
318         }
319
320         rc = 0;
321
322 prime_end:
323         EVP_PKEY_free(dh);
324         EVP_PKEY_CTX_free(ctx);
325         return rc;
326 }
327
328 int main(int argc, char **argv)
329 {
330         struct sk_keyfile_config *config;
331         char *datafile = NULL;
332         char *input = NULL;
333         char *load = NULL;
334         char *modify = NULL;
335         char *output = NULL;
336         char *mgsnids = NULL;
337         char *nodemap = NULL;
338         char *fsname = NULL;
339         char *tmp;
340         char *tmp2;
341         int crypt = SK_CRYPT_EMPTY;
342         int hmac = SK_HMAC_EMPTY;
343         int expire = -1;
344         int shared_keylen = -1;
345         int prime_bits = -1;
346         int verbose = 0;
347         int i;
348         int opt;
349         enum sk_key_type type = SK_TYPE_INVALID;
350         bool generate_prime = false;
351
352         static struct option long_opts[] = {
353         { .name = "crypt",      .has_arg = required_argument, .val = 'c'},
354         { .name = "data",       .has_arg = required_argument, .val = 'd'},
355         { .name = "expire",     .has_arg = required_argument, .val = 'e'},
356         { .name = "fsname",     .has_arg = required_argument, .val = 'f'},
357         { .name = "mgsnids",    .has_arg = required_argument, .val = 'g'},
358         { .name = "help",       .has_arg = no_argument,       .val = 'h'},
359         { .name = "hmac",       .has_arg = required_argument, .val = 'i'},
360         { .name = "integrity",  .has_arg = required_argument, .val = 'i'},
361         { .name = "key-bits",   .has_arg = required_argument, .val = 'k'},
362         { .name = "shared",     .has_arg = required_argument, .val = 'k'},
363         { .name = "load",       .has_arg = required_argument, .val = 'l'},
364         { .name = "modify",     .has_arg = required_argument, .val = 'm'},
365         { .name = "nodemap",    .has_arg = required_argument, .val = 'n'},
366         { .name = "prime-bits", .has_arg = required_argument, .val = 'p'},
367         { .name = "read",       .has_arg = required_argument, .val = 'r'},
368         { .name = "type",       .has_arg = required_argument, .val = 't'},
369         { .name = "verbose",    .has_arg = no_argument,       .val = 'v'},
370         { .name = "write",      .has_arg = required_argument, .val = 'w'},
371         { .name = NULL, } };
372
373         while ((opt = getopt_long(argc, argv,
374                                   "c:d:e:f:g:hi:l:m:n:p:r:s:k:t:w:v", long_opts,
375                                   NULL)) != EOF) {
376                 switch (opt) {
377                 case 'c':
378                         crypt = sk_name2crypt(optarg);
379                         break;
380                 case 'd':
381                         datafile = optarg;
382                         break;
383                 case 'e':
384                         expire = atoi(optarg);
385                         if (expire < 60)
386                                 fprintf(stderr, "warning: using a %us key "
387                                         "expiration may cause issues during "
388                                         "key renegotiation\n", expire);
389                         break;
390                 case 'f':
391                         fsname = optarg;
392                         if (strlen(fsname) > MTI_NAME_MAXLEN) {
393                                 fprintf(stderr,
394                                         "error: file system name longer than "
395                                         "%u characters\n", MTI_NAME_MAXLEN);
396                                 return EXIT_FAILURE;
397                         }
398                         break;
399                 case 'g':
400                         mgsnids = optarg;
401                         break;
402                 case 'h':
403                         usage(stdout, argv[0]);
404                         break;
405                 case 'i':
406                         hmac = sk_name2hmac(optarg);
407                         break;
408                 case 'k':
409                         shared_keylen = atoi(optarg);
410                         break;
411                 case 'l':
412                         load = optarg;
413                         break;
414                 case 'm':
415                         modify = optarg;
416                         break;
417                 case 'n':
418                         nodemap = optarg;
419                         if (strlen(nodemap) > LUSTRE_NODEMAP_NAME_LENGTH) {
420                                 fprintf(stderr,
421                                         "error: nodemap name longer than "
422                                         "%u characters\n",
423                                         LUSTRE_NODEMAP_NAME_LENGTH);
424                                 return EXIT_FAILURE;
425                         }
426                         break;
427                 case 'p':
428                         prime_bits = atoi(optarg);
429                         if (prime_bits <= 0) {
430                                 fprintf(stderr,
431                                         "error: invalid prime length: '%s'\n",
432                                         optarg);
433                                 return EXIT_FAILURE;
434                         }
435                         break;
436                 case 'r':
437                         input = optarg;
438                         break;
439                 case 't':
440                         tmp2 = strdup(optarg);
441                         if (!tmp2) {
442                                 fprintf(stderr,
443                                         "error: failed to allocate type\n");
444                                 return EXIT_FAILURE;
445                         }
446                         tmp = strsep(&tmp2, ",");
447                         while (tmp != NULL) {
448                                 if (strcasecmp(tmp, "server") == 0) {
449                                         type |= SK_TYPE_SERVER;
450                                 } else if (strcasecmp(tmp, "mgs") == 0) {
451                                         type |= SK_TYPE_MGS;
452                                 } else if (strcasecmp(tmp, "client") == 0) {
453                                         type |= SK_TYPE_CLIENT;
454                                 } else {
455                                         fprintf(stderr,
456                                                 "error: invalid type '%s', "
457                                                 "must be mgs, server, or client"
458                                                 "\n", optarg);
459                                         return EXIT_FAILURE;
460                                 }
461                                 tmp = strsep(&tmp2, ",");
462                         }
463                         free(tmp2);
464                         break;
465                 case 'v':
466                         verbose++;
467                         break;
468                 case 'w':
469                         output = optarg;
470                         break;
471                 default:
472                         fprintf(stderr, "error: unknown option: '%c'\n", opt);
473                         return EXIT_FAILURE;
474                         break;
475                 }
476         }
477
478         if (optind != argc) {
479                 fprintf(stderr,
480                         "error: extraneous arguments provided, check usage\n");
481                 return EXIT_FAILURE;
482         }
483
484         if (!input && !output && !load && !modify) {
485                 usage(stderr, argv[0]);
486                 return EXIT_FAILURE;
487         }
488
489         /* init gss logger for foreground (no syslog) which prints to stderr */
490         initerr(NULL, verbose, 1);
491
492         if (input)
493                 return print_config(input);
494
495         if (load) {
496                 if (sk_load_keyfile(load))
497                         return EXIT_FAILURE;
498                 return EXIT_SUCCESS;
499         }
500
501         if (crypt == SK_CRYPT_INVALID) {
502                 fprintf(stderr, "error: invalid crypt algorithm specified\n");
503                 return EXIT_FAILURE;
504         }
505         if (hmac == SK_HMAC_INVALID) {
506                 fprintf(stderr, "error: invalid HMAC algorithm specified\n");
507                 return EXIT_FAILURE;
508         }
509
510         if (modify && datafile) {
511                 fprintf(stderr,
512                         "error: data file option not valid in key modify\n");
513                 return EXIT_FAILURE;
514         }
515
516         if (modify) {
517                 config = sk_read_file(modify);
518                 if (!config)
519                         return EXIT_FAILURE;
520
521                 if (type != SK_TYPE_INVALID) {
522                         /* generate key when adding client type */
523                         if (!(config->skc_type & SK_TYPE_CLIENT) &&
524                             type & SK_TYPE_CLIENT)
525                                 generate_prime = true;
526                         else if (!(type & SK_TYPE_CLIENT))
527                                 memset(config->skc_p, 0, SK_MAX_P_BYTES);
528
529                         config->skc_type = type;
530                 }
531                 if (prime_bits != -1) {
532                         memset(config->skc_p, 0, SK_MAX_P_BYTES);
533                         if (config->skc_prime_bits != prime_bits &&
534                             config->skc_type & SK_TYPE_CLIENT)
535                                 generate_prime = true;
536                 }
537         } else {
538                 /* write mode for a new key */
539                 if (!fsname && !mgsnids) {
540                         fprintf(stderr,
541                                 "error: missing --fsname or --mgsnids\n");
542                         return EXIT_FAILURE;
543                 }
544
545                 config = calloc(1, sizeof(*config));
546                 if (!config)
547                         return EXIT_FAILURE;
548
549                 /* Set the defaults for new key */
550                 config->skc_version = SK_CONF_VERSION;
551                 config->skc_expire = SK_DEFAULT_EXPIRE;
552                 config->skc_shared_keylen = SK_DEFAULT_SK_KEYLEN;
553                 config->skc_prime_bits = SK_DEFAULT_PRIME_BITS;
554                 config->skc_crypt_alg = SK_CRYPT_AES256_CTR;
555                 config->skc_hmac_alg = SK_HMAC_SHA256;
556                 for (i = 0; i < MAX_MGSNIDS; i++)
557                         config->skc_mgsnids[i] = LNET_NID_ANY;
558
559                 if (type == SK_TYPE_INVALID) {
560                         fprintf(stderr, "error: no type specified for key\n");
561                         goto error;
562                 }
563                 config->skc_type = type;
564                 generate_prime = type & SK_TYPE_CLIENT;
565
566                 /* SK_DEFAULT_NODEMAP is made to fit in skc_nodemap */
567                 strcpy(config->skc_nodemap, SK_DEFAULT_NODEMAP);
568
569                 if (!datafile)
570                         datafile = "/dev/random";
571         }
572
573         if (crypt != SK_CRYPT_EMPTY)
574                 config->skc_crypt_alg = crypt;
575         if (hmac != SK_HMAC_EMPTY)
576                 config->skc_hmac_alg = hmac;
577         if (expire != -1)
578                 config->skc_expire = expire;
579         if (shared_keylen != -1)
580                 config->skc_shared_keylen = shared_keylen;
581         if (prime_bits != -1) {
582 #ifdef HAVE_OPENSSL_EVP_PKEY
583                 /* #define DH_MIN_MODULUS_BITS 512, not exported by OpenSSL */
584                 if (prime_bits < 512) {
585                         fprintf(stderr,
586                                 "error: prime length must be at least 512\n");
587                         return EXIT_FAILURE;
588                 }
589 #endif
590                 config->skc_prime_bits = prime_bits;
591         }
592         if (fsname)
593                 /* fsname string length was checked when parsing
594                  * command-line options
595                  */
596                 strcpy(config->skc_fsname, fsname);
597         if (nodemap)
598                 /* nodemap string length was checked when parsing
599                  * command-line options
600                  */
601                 strcpy(config->skc_nodemap, nodemap);
602         if (mgsnids && parse_mgsnids(mgsnids, config))
603                 goto error;
604         if (sk_validate_config(config)) {
605                 fprintf(stderr, "error: key configuration failed validation\n");
606                 goto error;
607         }
608
609         if (datafile && get_key_data(datafile, config->skc_shared_key,
610                                      config->skc_shared_keylen)) {
611                 fprintf(stderr, "error: failure getting key data from '%s'\n",
612                         datafile);
613                 goto error;
614         }
615
616         if (generate_prime) {
617                 printf("Generating DH parameters, this can take a while...\n");
618                 if (__gen_ssk_prime(config))
619                         goto error;
620         }
621
622         if (write_config_file(modify ?: output, config, modify))
623                 goto error;
624
625         return EXIT_SUCCESS;
626
627 error:
628         free(config);
629         return EXIT_FAILURE;
630 }