4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
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.
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).
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
23 * Copyright (C) 2015, Trustees of Indiana University
25 * Copyright (c) 2016, Intel Corporation.
27 * Author: Jeremy Filizetti <jfilizet@iu.edu>
40 #include <sys/types.h>
43 #include <linux/lustre/lustre_user.h>
52 /* One week default expiration */
53 #define SK_DEFAULT_EXPIRE 604800
54 /* But only one day in FIPS mode */
55 #define SK_DEFAULT_EXPIRE_FIPS 86400
56 #define SK_DEFAULT_SK_KEYLEN 256
57 #define SK_DEFAULT_PRIME_BITS 2048
58 #define SK_DEFAULT_NODEMAP "default"
62 static void usage(FILE *fp, char *program)
66 fprintf(fp, "Usage %s [OPTIONS] {-l|-m|-r|-w} <keyfile>\n", program);
67 fprintf(fp, "-l|--load <keyfile> Load key from file into user's "
69 fprintf(fp, "-m|--modify <keyfile> Modify keyfile's attributes\n");
70 fprintf(fp, "-r|--read <keyfile> Show keyfile's attributes\n");
71 fprintf(fp, "-w|--write <keyfile> Generate keyfile\n\n");
72 fprintf(fp, "Modify/Write Options:\n");
73 fprintf(fp, "-c|--crypt <num> Cipher for encryption "
74 "(Default: AES Counter mode)\n");
75 for (i = 1; i < ARRAY_SIZE(sk_crypt_algs); i++)
77 sk_crypt_algs[i].sct_name);
78 fprintf(fp, "-i|--hmac <num> Hash algorithm for integrity "
79 "(Default: SHA256)\n");
80 for (i = 1; i < ARRAY_SIZE(sk_hmac_algs); i++)
82 sk_hmac_algs[i].sht_name);
83 fprintf(fp, "-e|--expire <num> Seconds before contexts from "
84 "key expire (Default: %d seconds (%.3g days))\n",
85 SK_DEFAULT_EXPIRE, (double)SK_DEFAULT_EXPIRE / 3600 / 24);
86 fprintf(fp, "-f|--fsname <name> File system name for key\n");
87 fprintf(fp, "-g|--mgsnids <nids> Comma seperated list of MGS "
88 "NIDs. Only required when mgssec is used (Default: \"\")\n");
89 fprintf(fp, "-n|--nodemap <name> Nodemap name for key "
90 "(Default: \"%s\")\n", SK_DEFAULT_NODEMAP);
91 fprintf(fp, "-p|--prime-bits <len> Prime length (p) for DHKE in "
92 "bits (Default: %d)\n", SK_DEFAULT_PRIME_BITS);
93 fprintf(fp, "-t|--type <type> Key type (mgs, server, "
95 fprintf(fp, "-k|--key-bits <len> Shared key length in bits "
96 "(Default: %d)\n", SK_DEFAULT_SK_KEYLEN);
97 fprintf(fp, "-d|--data <file> Key data source for new keys "
98 "(Default: /dev/random)\n");
99 fprintf(fp, " Not a seed value. "
100 "This is the actual key value.\n\n");
101 fprintf(fp, "Other Options:\n");
102 fprintf(fp, "-v|--verbose Increase verbosity for errors\n");
106 static ssize_t get_key_data(char *src, void *buffer, size_t bits)
113 /* convert bits to minimum number of bytes */
114 remain = (bits + 7) / 8;
116 printf("Reading random data for shared key from '%s'\n", src);
117 fd = open(src, O_RDONLY);
119 fprintf(stderr, "error: opening '%s': %s\n", src,
125 rc = read(fd, ptr, remain);
129 fprintf(stderr, "error: reading from '%s': %s\n", src,
134 } else if (rc == 0) {
136 "error: key source too short for %zd-bit key\n",
151 static int write_config_file(char *output_file,
152 struct sk_keyfile_config *config, bool overwrite)
156 int flags = O_WRONLY | O_CREAT;
161 sk_config_cpu_to_disk(config);
163 fd = open(output_file, flags, 0400);
165 fprintf(stderr, "error: opening '%s': %s\n", output_file,
170 rc = write(fd, config, sizeof(*config));
172 fprintf(stderr, "error: writing to '%s': %s\n", output_file,
175 } else if (rc != sizeof(*config)) {
176 fprintf(stderr, "error: short write to '%s'\n", output_file);
187 static int print_config(char *filename)
189 struct sk_keyfile_config *config;
192 config = sk_read_file(filename);
196 if (sk_validate_config(config)) {
197 fprintf(stderr, "error: key configuration failed validation\n");
202 printf("Version: %u\n", config->skc_version);
204 if (config->skc_type & SK_TYPE_MGS)
206 if (config->skc_type & SK_TYPE_SERVER)
208 if (config->skc_type & SK_TYPE_CLIENT)
211 printf("HMAC alg: %s\n", sk_hmac2name(config->skc_hmac_alg));
212 printf("Crypto alg: %s\n", sk_crypt2name(config->skc_crypt_alg));
213 printf("Ctx Expiration: %u seconds\n", config->skc_expire);
214 printf("Shared keylen: %u bits\n", config->skc_shared_keylen);
215 printf("Prime length: %u bits\n", config->skc_prime_bits);
216 printf("File system: %s\n", config->skc_fsname);
217 printf("MGS NIDs: ");
218 for (i = 0; i < MAX_MGSNIDS; i++) {
219 if (config->skc_mgsnids[i] == LNET_NID_ANY)
221 printf(" %s", libcfs_nid2str(config->skc_mgsnids[i]));
224 printf("Nodemap name: %s\n", config->skc_nodemap);
225 printf("Shared key:\n");
226 print_hex(0, config->skc_shared_key, config->skc_shared_keylen / 8);
228 /* Don't print empty keys */
229 for (i = 0; i < SK_MAX_P_BYTES; i++)
230 if (config->skc_p[i] != 0)
233 if (i != SK_MAX_P_BYTES) {
234 printf("Prime (p):\n");
235 print_hex(0, config->skc_p, config->skc_prime_bits / 8);
242 static int parse_mgsnids(char *mgsnids, struct sk_keyfile_config *config)
251 /* replace all old values */
252 for (i = 0; i < MAX_MGSNIDS; i++)
253 config->skc_mgsnids[i] = LNET_NID_ANY;
256 end = mgsnids + strlen(mgsnids);
258 while (ptr < end && i < MAX_MGSNIDS) {
259 sep = strstr(ptr, ",");
263 nid = libcfs_str2nid(ptr);
264 if (nid == LNET_NID_ANY) {
265 fprintf(stderr, "error: invalid MGS NID: %s\n", ptr);
270 config->skc_mgsnids[i++] = nid;
271 ptr += strlen(ptr) + 1;
274 if (i == MAX_MGSNIDS) {
275 fprintf(stderr, "error: more than %u MGS NIDs provided\n", i);
282 #if !defined(HAVE_OPENSSL_EVP_PKEY) && OPENSSL_VERSION_NUMBER >= 0x10100000L
283 static inline int __fetch_ssk_prime(struct sk_keyfile_config *config)
290 primenid = sk_primebits2primenid(config->skc_prime_bits);
291 dh = DH_new_by_nid(primenid);
293 fprintf(stderr, "error: dh cannot be init\n");
299 fprintf(stderr, "error: cannot get p from dh\n");
303 if (BN_num_bytes(p) > SK_MAX_P_BYTES) {
305 "error: requested length %d exceeds maximum %d\n",
306 BN_num_bytes(p), SK_MAX_P_BYTES * 8);
310 if (BN_bn2bin(p, config->skc_p) != BN_num_bytes(p)) {
311 fprintf(stderr, "error: convert BIGNUM p to binary failed\n");
320 "error: fetching SSK prime failed: %s\n",
321 ERR_error_string(ERR_get_error(), NULL));
327 static inline int __gen_ssk_prime(struct sk_keyfile_config *config)
330 const char *primename;
331 EVP_PKEY_CTX *ctx = NULL;
336 primename = sk_primebits2name(config->skc_prime_bits);
339 "error: prime len %d not supported in FIPS mode\n",
340 config->skc_prime_bits);
343 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
345 "FIPS mode, using well-known prime %s\n", primename);
346 #ifndef HAVE_OPENSSL_EVP_PKEY
347 return __fetch_ssk_prime(config);
349 #endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
352 ctx = EVP_PKEY_CTX_new_from_name(NULL, "DH", NULL);
353 if (!ctx || EVP_PKEY_paramgen_init(ctx) != 1) {
354 fprintf(stderr, "error: ctx cannot be init\n");
358 if (EVP_PKEY_CTX_set_dh_paramgen_prime_len(ctx,
359 config->skc_prime_bits) <= 0 ||
360 EVP_PKEY_CTX_set_dh_paramgen_generator(ctx, SK_GENERATOR) <= 0) {
361 fprintf(stderr, "error: cannot set prime or generator\n");
365 if (EVP_PKEY_paramgen(ctx, &dh) != 1) {
366 fprintf(stderr, "error: cannot generate DH parameters\n");
370 if (!EVP_PKEY_get_bn_param(dh, OSSL_PKEY_PARAM_FFC_P, &p)) {
371 fprintf(stderr, "error: cannot get p from dh\n");
375 if (BN_num_bytes(p) > SK_MAX_P_BYTES) {
377 "error: cannot generate DH parameters: requested length %d exceeds maximum %d\n",
378 config->skc_prime_bits, SK_MAX_P_BYTES * 8);
381 if (BN_bn2bin(p, config->skc_p) != BN_num_bytes(p)) {
383 "error: convert BIGNUM p to binary failed\n");
392 "error: generating SSK prime failed: %s\n",
393 ERR_error_string(ERR_get_error(), NULL));
395 EVP_PKEY_CTX_free(ctx);
399 int main(int argc, char **argv)
401 struct sk_keyfile_config *config;
402 char *datafile = NULL;
407 char *mgsnids = NULL;
408 char *nodemap = NULL;
412 int crypt = SK_CRYPT_EMPTY;
413 int hmac = SK_HMAC_EMPTY;
415 int shared_keylen = -1;
420 enum sk_key_type type = SK_TYPE_INVALID;
421 bool generate_prime = false;
423 static struct option long_opts[] = {
424 { .name = "crypt", .has_arg = required_argument, .val = 'c'},
425 { .name = "data", .has_arg = required_argument, .val = 'd'},
426 { .name = "expire", .has_arg = required_argument, .val = 'e'},
427 { .name = "fsname", .has_arg = required_argument, .val = 'f'},
428 { .name = "mgsnids", .has_arg = required_argument, .val = 'g'},
429 { .name = "help", .has_arg = no_argument, .val = 'h'},
430 { .name = "hmac", .has_arg = required_argument, .val = 'i'},
431 { .name = "integrity", .has_arg = required_argument, .val = 'i'},
432 { .name = "key-bits", .has_arg = required_argument, .val = 'k'},
433 { .name = "shared", .has_arg = required_argument, .val = 'k'},
434 { .name = "load", .has_arg = required_argument, .val = 'l'},
435 { .name = "modify", .has_arg = required_argument, .val = 'm'},
436 { .name = "nodemap", .has_arg = required_argument, .val = 'n'},
437 { .name = "prime-bits", .has_arg = required_argument, .val = 'p'},
438 { .name = "read", .has_arg = required_argument, .val = 'r'},
439 { .name = "type", .has_arg = required_argument, .val = 't'},
440 { .name = "verbose", .has_arg = no_argument, .val = 'v'},
441 { .name = "write", .has_arg = required_argument, .val = 'w'},
444 while ((opt = getopt_long(argc, argv,
445 "c:d:e:f:g:hi:l:m:n:p:r:s:k:t:w:v", long_opts,
449 crypt = sk_name2crypt(optarg);
455 expire = atoi(optarg);
457 fprintf(stderr, "warning: using a %us key "
458 "expiration may cause issues during "
459 "key renegotiation\n", expire);
463 if (strlen(fsname) > MTI_NAME_MAXLEN) {
465 "error: file system name longer than "
466 "%u characters\n", MTI_NAME_MAXLEN);
474 usage(stdout, argv[0]);
477 hmac = sk_name2hmac(optarg);
480 shared_keylen = atoi(optarg);
490 if (strlen(nodemap) > LUSTRE_NODEMAP_NAME_LENGTH) {
492 "error: nodemap name longer than "
494 LUSTRE_NODEMAP_NAME_LENGTH);
499 prime_bits = atoi(optarg);
500 if (prime_bits <= 0) {
502 "error: invalid prime length: '%s'\n",
511 tmp2 = strdup(optarg);
514 "error: failed to allocate type\n");
517 tmp = strsep(&tmp2, ",");
518 while (tmp != NULL) {
519 if (strcasecmp(tmp, "server") == 0) {
520 type |= SK_TYPE_SERVER;
521 } else if (strcasecmp(tmp, "mgs") == 0) {
523 } else if (strcasecmp(tmp, "client") == 0) {
524 type |= SK_TYPE_CLIENT;
527 "error: invalid type '%s', "
528 "must be mgs, server, or client"
532 tmp = strsep(&tmp2, ",");
543 fprintf(stderr, "error: unknown option: '%c'\n", opt);
549 if (optind != argc) {
551 "error: extraneous arguments provided, check usage\n");
555 if (!input && !output && !load && !modify) {
556 usage(stderr, argv[0]);
560 /* init gss logger for foreground (no syslog) which prints to stderr */
561 initerr(NULL, verbose, 1);
563 fips_mode = FIPS_mode();
566 return print_config(input);
569 if (sk_load_keyfile(load))
574 if (crypt == SK_CRYPT_INVALID) {
575 fprintf(stderr, "error: invalid crypt algorithm specified\n");
578 if (hmac == SK_HMAC_INVALID) {
579 fprintf(stderr, "error: invalid HMAC algorithm specified\n");
583 if (modify && datafile) {
585 "error: data file option not valid in key modify\n");
590 config = sk_read_file(modify);
594 if (type != SK_TYPE_INVALID) {
595 /* generate key when adding client type */
596 if (!(config->skc_type & SK_TYPE_CLIENT) &&
597 type & SK_TYPE_CLIENT)
598 generate_prime = true;
599 else if (!(type & SK_TYPE_CLIENT))
600 memset(config->skc_p, 0, SK_MAX_P_BYTES);
602 config->skc_type = type;
604 if (prime_bits != -1) {
605 memset(config->skc_p, 0, SK_MAX_P_BYTES);
606 if (config->skc_prime_bits != prime_bits &&
607 config->skc_type & SK_TYPE_CLIENT)
608 generate_prime = true;
611 /* write mode for a new key */
612 if (!fsname && !mgsnids) {
614 "error: missing --fsname or --mgsnids\n");
618 config = calloc(1, sizeof(*config));
622 /* Set the defaults for new key */
623 config->skc_version = SK_CONF_VERSION;
624 config->skc_expire = fips_mode ?
625 SK_DEFAULT_EXPIRE_FIPS : SK_DEFAULT_EXPIRE;
626 config->skc_shared_keylen = SK_DEFAULT_SK_KEYLEN;
627 config->skc_prime_bits = SK_DEFAULT_PRIME_BITS;
628 config->skc_crypt_alg = SK_CRYPT_AES256_CTR;
629 config->skc_hmac_alg = SK_HMAC_SHA256;
630 for (i = 0; i < MAX_MGSNIDS; i++)
631 config->skc_mgsnids[i] = LNET_NID_ANY;
633 if (type == SK_TYPE_INVALID) {
634 fprintf(stderr, "error: no type specified for key\n");
637 config->skc_type = type;
638 generate_prime = type & SK_TYPE_CLIENT;
640 /* SK_DEFAULT_NODEMAP is made to fit in skc_nodemap */
641 strcpy(config->skc_nodemap, SK_DEFAULT_NODEMAP);
644 datafile = "/dev/random";
647 if (crypt != SK_CRYPT_EMPTY)
648 config->skc_crypt_alg = crypt;
649 if (hmac != SK_HMAC_EMPTY)
650 config->skc_hmac_alg = hmac;
652 config->skc_expire = expire;
653 if (fips_mode && config->skc_expire > SK_DEFAULT_EXPIRE_FIPS)
655 "warning: using a %us key expiration greater than %us is not recommended in FIPS mode\n",
656 config->skc_expire, SK_DEFAULT_EXPIRE_FIPS);
657 if (shared_keylen != -1)
658 config->skc_shared_keylen = shared_keylen;
659 if (prime_bits != -1) {
660 #ifdef HAVE_OPENSSL_EVP_PKEY
661 /* #define DH_MIN_MODULUS_BITS 512, not exported by OpenSSL */
662 if (prime_bits < 512) {
664 "error: prime length must be at least 512\n");
668 config->skc_prime_bits = prime_bits;
671 /* fsname string length was checked when parsing
672 * command-line options
674 strcpy(config->skc_fsname, fsname);
676 /* nodemap string length was checked when parsing
677 * command-line options
679 strcpy(config->skc_nodemap, nodemap);
680 if (mgsnids && parse_mgsnids(mgsnids, config))
682 if (sk_validate_config(config)) {
683 fprintf(stderr, "error: key configuration failed validation\n");
687 if (datafile && get_key_data(datafile, config->skc_shared_key,
688 config->skc_shared_keylen)) {
689 fprintf(stderr, "error: failure getting key data from '%s'\n",
694 if (generate_prime) {
695 printf("Generating DH parameters, this can take a while...\n");
696 if (__gen_ssk_prime(config))
700 if (write_config_file(modify ?: output, config, modify))