Whamcloud - gitweb
825146eb78d318a4f0aec84861922440ab12195f
[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 data source for new keys "
137                 "(Default: /dev/random)\n");
138         fprintf(fp, "                        Not a seed value.  This is the actual key value.\n\n");
139         fprintf(fp, "Other Options:\n");
140         fprintf(fp, "-v|--verbose           Increase verbosity for errors\n");
141         exit(EXIT_FAILURE);
142 }
143
144 static ssize_t get_key_data(char *src, void *buffer, size_t bits)
145 {
146         char *ptr = buffer;
147         size_t remain;
148         ssize_t rc;
149         int fd;
150
151         /* convert bits to minimum number of bytes */
152         remain = (bits + 7) / 8;
153
154         printf("Reading random data for shared key from '%s'\n", src);
155         fd = open(src, O_RDONLY);
156         if (fd < 0) {
157                 fprintf(stderr, "error: opening '%s': %s\n", src,
158                         strerror(errno));
159                 return -errno;
160         }
161
162         while (remain > 0) {
163                 rc = read(fd, ptr, remain);
164                 if (rc < 0) {
165                         if (errno == EINTR)
166                                 continue;
167                         fprintf(stderr, "error: reading from '%s': %s\n", src,
168                                 strerror(errno));
169                         rc = -errno;
170                         goto out;
171
172                 } else if (rc == 0) {
173                         fprintf(stderr,
174                                 "error: key source too short for %zd-bit key\n",
175                                 bits);
176                         rc = -ENODATA;
177                         goto out;
178                 }
179                 ptr += rc;
180                 remain -= rc;
181         }
182         rc = 0;
183
184 out:
185         close(fd);
186         return rc;
187 }
188
189 static int write_config_file(char *output_file,
190                              struct sk_keyfile_config *config, bool overwrite)
191 {
192         size_t rc;
193         int fd;
194         int flags = O_WRONLY | O_CREAT;
195
196         if (!overwrite)
197                 flags |= O_EXCL;
198
199         sk_config_cpu_to_disk(config);
200
201         fd = open(output_file, flags, 0400);
202         if (fd < 0) {
203                 fprintf(stderr, "error: opening '%s': %s\n", output_file,
204                         strerror(errno));
205                 return -errno;
206         }
207
208         rc = write(fd, config, sizeof(*config));
209         if (rc < 0) {
210                 fprintf(stderr, "error: writing to '%s': %s\n", output_file,
211                         strerror(errno));
212                 rc = -errno;
213         } else if (rc != sizeof(*config)) {
214                 fprintf(stderr, "error: short write to '%s'\n", output_file);
215                 rc = -ENOSPC;
216
217         } else {
218                 rc = 0;
219         }
220
221         close(fd);
222         return rc;
223 }
224
225 static int print_config(char *filename)
226 {
227         struct sk_keyfile_config *config;
228         int i;
229
230         config = sk_read_file(filename);
231         if (!config)
232                 return EXIT_FAILURE;
233
234         if (sk_validate_config(config)) {
235                 fprintf(stderr, "error: key configuration failed validation\n");
236                 free(config);
237                 return EXIT_FAILURE;
238         }
239
240         printf("Version:        %u\n", config->skc_version);
241         printf("Type:          ");
242         if (config->skc_type & SK_TYPE_MGS)
243                 printf(" mgs");
244         if (config->skc_type & SK_TYPE_SERVER)
245                 printf(" server");
246         if (config->skc_type & SK_TYPE_CLIENT)
247                 printf(" client");
248         printf("\n");
249         printf("HMAC alg:       %s\n", sk_hmac2name[config->skc_hmac_alg]);
250         printf("Crypto alg:     %s\n", cfs_crypto_hash_name(config->skc_hmac_alg));
251         printf("Ctx Expiration: %u seconds\n", config->skc_expire);
252         printf("Shared keylen:  %u bits\n", config->skc_shared_keylen);
253         printf("Prime length:   %u bits\n", config->skc_prime_bits);
254         printf("File system:    %s\n", config->skc_fsname);
255         printf("MGS NIDs:      ");
256         for (i = 0; i < MAX_MGSNIDS; i++) {
257                 if (config->skc_mgsnids[i] == LNET_NID_ANY)
258                         continue;
259                 printf(" %s", libcfs_nid2str(config->skc_mgsnids[i]));
260         }
261         printf("\n");
262         printf("Nodemap name:   %s\n", config->skc_nodemap);
263         printf("Shared key:\n");
264         print_hex(0, config->skc_shared_key, config->skc_shared_keylen / 8);
265
266         /* Don't print empty keys */
267         for (i = 0; i < SK_MAX_P_BYTES; i++)
268                 if (config->skc_p[i] != 0)
269                         break;
270
271         if (i != SK_MAX_P_BYTES) {
272                 printf("Prime (p):\n");
273                 print_hex(0, config->skc_p, config->skc_prime_bits / 8);
274         }
275
276         free(config);
277         return EXIT_SUCCESS;
278 }
279
280 static int parse_mgsnids(char *mgsnids, struct sk_keyfile_config *config)
281 {
282         lnet_nid_t nid;
283         char *ptr;
284         char *sep;
285         char *end;
286         int rc = 0;
287         int i;
288
289         /* replace all old values */
290         for (i = 0; i < MAX_MGSNIDS; i++)
291                 config->skc_mgsnids[i] = LNET_NID_ANY;
292
293         i = 0;
294         end = mgsnids + strlen(mgsnids);
295         ptr = mgsnids;
296         while (ptr < end && i < MAX_MGSNIDS) {
297                 sep = strstr(ptr, ",");
298                 if (sep != NULL)
299                         *sep = '\0';
300
301                 nid = libcfs_str2nid(ptr);
302                 if (nid == LNET_NID_ANY) {
303                         fprintf(stderr, "error: invalid MGS NID: %s\n", ptr);
304                         rc = -EINVAL;
305                         break;
306                 }
307
308                 config->skc_mgsnids[i++] = nid;
309                 ptr += strlen(ptr) + 1;
310         }
311
312         if (i == MAX_MGSNIDS) {
313                 fprintf(stderr, "error: more than %u MGS NIDs provided\n", i);
314                 rc = -E2BIG;
315         }
316
317         return rc;
318 }
319
320 int main(int argc, char **argv)
321 {
322         struct sk_keyfile_config *config;
323         char *datafile = NULL;
324         char *input = NULL;
325         char *load = NULL;
326         char *modify = NULL;
327         char *output = NULL;
328         char *mgsnids = NULL;
329         char *nodemap = NULL;
330         char *fsname = NULL;
331         char *tmp;
332         char *tmp2;
333         int crypt = SK_CRYPT_EMPTY;
334         enum cfs_crypto_hash_alg hmac = CFS_HASH_ALG_NULL;
335         int expire = -1;
336         int shared_keylen = -1;
337         int prime_bits = -1;
338         int verbose = 0;
339         int i;
340         int opt;
341         enum sk_key_type  type = SK_TYPE_INVALID;
342         bool generate_prime = false;
343         DH *dh;
344
345         static struct option long_opts[] = {
346         { .name = "crypt",      .has_arg = required_argument, .val = 'c'},
347         { .name = "data",       .has_arg = required_argument, .val = 'd'},
348         { .name = "expire",     .has_arg = required_argument, .val = 'e'},
349         { .name = "fsname",     .has_arg = required_argument, .val = 'f'},
350         { .name = "mgsnids",    .has_arg = required_argument, .val = 'g'},
351         { .name = "help",       .has_arg = no_argument,       .val = 'h'},
352         { .name = "hmac",       .has_arg = required_argument, .val = 'i'},
353         { .name = "integrity",  .has_arg = required_argument, .val = 'i'},
354         { .name = "key-bits",   .has_arg = required_argument, .val = 'k'},
355         { .name = "shared",     .has_arg = required_argument, .val = 'k'},
356         { .name = "load",       .has_arg = required_argument, .val = 'l'},
357         { .name = "modify",     .has_arg = required_argument, .val = 'm'},
358         { .name = "nodemap",    .has_arg = required_argument, .val = 'n'},
359         { .name = "prime-bits", .has_arg = required_argument, .val = 'p'},
360         { .name = "read",       .has_arg = required_argument, .val = 'r'},
361         { .name = "type",       .has_arg = required_argument, .val = 't'},
362         { .name = "verbose",    .has_arg = no_argument,       .val = 'v'},
363         { .name = "write",      .has_arg = required_argument, .val = 'w'},
364         { .name = NULL, } };
365
366         while ((opt = getopt_long(argc, argv,
367                                   "c:d:e:f:g:hi:l:m:n:p:r:s:k:t:w:v", long_opts,
368                                   NULL)) != EOF) {
369                 switch (opt) {
370                 case 'c':
371                         crypt = sk_name2crypt(optarg);
372                         break;
373                 case 'd':
374                         datafile = optarg;
375                         break;
376                 case 'e':
377                         expire = atoi(optarg);
378                         if (expire < 60)
379                                 fprintf(stderr, "warning: using a %us key "
380                                         "expiration may cause issues during "
381                                         "key renegotiation\n", expire);
382                         break;
383                 case 'f':
384                         fsname = optarg;
385                         if (strlen(fsname) > MTI_NAME_MAXLEN) {
386                                 fprintf(stderr,
387                                         "error: file system name longer than "
388                                         "%u characters\n", MTI_NAME_MAXLEN);
389                                 return EXIT_FAILURE;
390                         }
391                         break;
392                 case 'g':
393                         mgsnids = optarg;
394                         break;
395                 case 'h':
396                         usage(stdout, argv[0]);
397                         break;
398                 case 'i':
399                         hmac = sk_name2hmac(optarg);
400                         break;
401                 case 'k':
402                         shared_keylen = atoi(optarg);
403                         break;
404                 case 'l':
405                         load = optarg;
406                         break;
407                 case 'm':
408                         modify = optarg;
409                         break;
410                 case 'n':
411                         nodemap = optarg;
412                         if (strlen(nodemap) > LUSTRE_NODEMAP_NAME_LENGTH) {
413                                 fprintf(stderr,
414                                         "error: nodemap name longer than "
415                                         "%u characters\n",
416                                         LUSTRE_NODEMAP_NAME_LENGTH);
417                                 return EXIT_FAILURE;
418                         }
419                         break;
420                 case 'p':
421                         prime_bits = atoi(optarg);
422                         if (prime_bits <= 0) {
423                                 fprintf(stderr,
424                                         "error: invalid prime length: '%s'\n",
425                                         optarg);
426                                 return EXIT_FAILURE;
427                         }
428                         break;
429                 case 'r':
430                         input = optarg;
431                         break;
432                 case 't':
433                         tmp2 = strdup(optarg);
434                         if (!tmp2) {
435                                 fprintf(stderr,
436                                         "error: failed to allocate type\n");
437                                 return EXIT_FAILURE;
438                         }
439                         tmp = strsep(&tmp2, ",");
440                         while (tmp != NULL) {
441                                 if (strcasecmp(tmp, "server") == 0) {
442                                         type |= SK_TYPE_SERVER;
443                                 } else if (strcasecmp(tmp, "mgs") == 0) {
444                                         type |= SK_TYPE_MGS;
445                                 } else if (strcasecmp(tmp, "client") == 0) {
446                                         type |= SK_TYPE_CLIENT;
447                                 } else {
448                                         fprintf(stderr,
449                                                 "error: invalid type '%s', "
450                                                 "must be mgs, server, or client"
451                                                 "\n", optarg);
452                                         return EXIT_FAILURE;
453                                 }
454                                 tmp = strsep(&tmp2, ",");
455                         }
456                         free(tmp2);
457                         break;
458                 case 'v':
459                         verbose++;
460                         break;
461                 case 'w':
462                         output = optarg;
463                         break;
464                 default:
465                         fprintf(stderr, "error: unknown option: '%c'\n", opt);
466                         return EXIT_FAILURE;
467                         break;
468                 }
469         }
470
471         if (optind != argc) {
472                 fprintf(stderr,
473                         "error: extraneous arguments provided, check usage\n");
474                 return EXIT_FAILURE;
475         }
476
477         if (!input && !output && !load && !modify) {
478                 usage(stderr, argv[0]);
479                 return EXIT_FAILURE;
480         }
481
482         /* init gss logger for foreground (no syslog) which prints to stderr */
483         initerr(NULL, verbose, 1);
484
485         if (input)
486                 return print_config(input);
487
488         if (load) {
489                 if (sk_load_keyfile(load))
490                         return EXIT_FAILURE;
491                 return EXIT_SUCCESS;
492         }
493
494         if (crypt == SK_CRYPT_INVALID) {
495                 fprintf(stderr, "error: invalid crypt algorithm specified\n");
496                 return EXIT_FAILURE;
497         }
498         if (hmac == SK_HMAC_INVALID) {
499                 fprintf(stderr, "error: invalid HMAC algorithm specified\n");
500                 return EXIT_FAILURE;
501         }
502         if (modify && datafile) {
503                 fprintf(stderr, "error: data file option not valid in key modify\n");
504                 return EXIT_FAILURE;
505         }
506
507         if (modify) {
508                 config = sk_read_file(modify);
509                 if (!config)
510                         return EXIT_FAILURE;
511
512                 if (type != SK_TYPE_INVALID) {
513                         /* generate key when adding client type */
514                         if (!(config->skc_type & SK_TYPE_CLIENT) &&
515                             type & SK_TYPE_CLIENT)
516                                 generate_prime = true;
517                         else if (!(type & SK_TYPE_CLIENT))
518                                 memset(config->skc_p, 0, SK_MAX_P_BYTES);
519
520                         config->skc_type = type;
521                 }
522                 if (prime_bits != -1) {
523                         memset(config->skc_p, 0, SK_MAX_P_BYTES);
524                         if (config->skc_prime_bits != prime_bits &&
525                             config->skc_type & SK_TYPE_CLIENT)
526                                 generate_prime = true;
527                 }
528         } else {
529                 /* write mode for a new key */
530                 if (!fsname && !mgsnids) {
531                         fprintf(stderr,
532                                 "error: missing --fsname or --mgsnids\n");
533                         return EXIT_FAILURE;
534                 }
535
536                 config = calloc(1, sizeof(*config));
537                 if (!config)
538                         return EXIT_FAILURE;
539
540                 /* Set the defaults for new key */
541                 config->skc_version = SK_CONF_VERSION;
542                 config->skc_expire = SK_DEFAULT_EXPIRE;
543                 config->skc_shared_keylen = SK_DEFAULT_SK_KEYLEN;
544                 config->skc_prime_bits = SK_DEFAULT_PRIME_BITS;
545                 config->skc_crypt_alg = SK_CRYPT_AES256_CTR;
546                 config->skc_hmac_alg = CFS_HASH_ALG_SHA256;
547                 for (i = 0; i < MAX_MGSNIDS; i++)
548                         config->skc_mgsnids[i] = LNET_NID_ANY;
549
550                 if (type == SK_TYPE_INVALID) {
551                         fprintf(stderr, "error: no type specified for key\n");
552                         goto error;
553                 }
554                 config->skc_type = type;
555                 generate_prime = type & SK_TYPE_CLIENT;
556
557                 strncpy(config->skc_nodemap, SK_DEFAULT_NODEMAP,
558                         sizeof(config->skc_nodemap) - 1);
559
560                 if (!datafile)
561                         datafile = "/dev/random";
562         }
563
564         if (crypt != SK_CRYPT_EMPTY)
565                 config->skc_crypt_alg = crypt;
566         if (hmac != CFS_HASH_ALG_NULL)
567                 config->skc_hmac_alg = hmac;
568         if (expire != -1)
569                 config->skc_expire = expire;
570         if (shared_keylen != -1)
571                 config->skc_shared_keylen = shared_keylen;
572         if (prime_bits != -1)
573                 config->skc_prime_bits = prime_bits;
574         if (fsname)
575                 strncpy(config->skc_fsname, fsname,
576                         sizeof(config->skc_fsname) - 1);
577         if (nodemap)
578                 strncpy(config->skc_nodemap, nodemap,
579                         sizeof(config->skc_nodemap) - 1);
580         if (mgsnids && parse_mgsnids(mgsnids, config))
581                 goto error;
582         if (sk_validate_config(config)) {
583                 fprintf(stderr, "error: key configuration failed validation\n");
584                 goto error;
585         }
586
587         if (datafile && get_key_data(datafile, config->skc_shared_key,
588                                      config->skc_shared_keylen)) {
589                 fprintf(stderr, "error: failure getting key data from '%s'\n",
590                         datafile);
591                 goto error;
592         }
593
594         if (generate_prime) {
595                 printf("Generating DH parameters, this can take a while...\n");
596                 dh = DH_generate_parameters(config->skc_prime_bits,
597                                             SK_GENERATOR, NULL, NULL);
598                 if (BN_num_bytes(dh->p) > SK_MAX_P_BYTES) {
599                         fprintf(stderr, "error: cannot generate DH parameters: "
600                                 "requested length %d exceeds maximum %d\n",
601                                 config->skc_prime_bits, SK_MAX_P_BYTES * 8);
602                         goto error;
603                 }
604                 if (BN_bn2bin(dh->p, config->skc_p) != BN_num_bytes(dh->p)) {
605                         fprintf(stderr,
606                                 "error: convert BIGNUM p to binary failed\n");
607                         goto error;
608                 }
609         }
610
611         if (write_config_file(modify ?: output, config, modify))
612                 goto error;
613
614         return EXIT_SUCCESS;
615
616 error:
617         free(config);
618         return EXIT_FAILURE;
619 }