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