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