Whamcloud - gitweb
LU-9795 gss: properly handle mgssec
[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 int main(int argc, char **argv)
279 {
280         struct sk_keyfile_config *config;
281         char *datafile = NULL;
282         char *input = NULL;
283         char *load = NULL;
284         char *modify = NULL;
285         char *output = NULL;
286         char *mgsnids = NULL;
287         char *nodemap = NULL;
288         char *fsname = NULL;
289         char *tmp;
290         char *tmp2;
291         int crypt = SK_CRYPT_EMPTY;
292         int hmac = SK_HMAC_EMPTY;
293         int expire = -1;
294         int shared_keylen = -1;
295         int prime_bits = -1;
296         int verbose = 0;
297         int i;
298         int opt;
299         enum sk_key_type type = SK_TYPE_INVALID;
300         bool generate_prime = false;
301         DH *dh;
302
303         static struct option long_opts[] = {
304         { .name = "crypt",      .has_arg = required_argument, .val = 'c'},
305         { .name = "data",       .has_arg = required_argument, .val = 'd'},
306         { .name = "expire",     .has_arg = required_argument, .val = 'e'},
307         { .name = "fsname",     .has_arg = required_argument, .val = 'f'},
308         { .name = "mgsnids",    .has_arg = required_argument, .val = 'g'},
309         { .name = "help",       .has_arg = no_argument,       .val = 'h'},
310         { .name = "hmac",       .has_arg = required_argument, .val = 'i'},
311         { .name = "integrity",  .has_arg = required_argument, .val = 'i'},
312         { .name = "key-bits",   .has_arg = required_argument, .val = 'k'},
313         { .name = "shared",     .has_arg = required_argument, .val = 'k'},
314         { .name = "load",       .has_arg = required_argument, .val = 'l'},
315         { .name = "modify",     .has_arg = required_argument, .val = 'm'},
316         { .name = "nodemap",    .has_arg = required_argument, .val = 'n'},
317         { .name = "prime-bits", .has_arg = required_argument, .val = 'p'},
318         { .name = "read",       .has_arg = required_argument, .val = 'r'},
319         { .name = "type",       .has_arg = required_argument, .val = 't'},
320         { .name = "verbose",    .has_arg = no_argument,       .val = 'v'},
321         { .name = "write",      .has_arg = required_argument, .val = 'w'},
322         { .name = NULL, } };
323
324         while ((opt = getopt_long(argc, argv,
325                                   "c:d:e:f:g:hi:l:m:n:p:r:s:k:t:w:v", long_opts,
326                                   NULL)) != EOF) {
327                 switch (opt) {
328                 case 'c':
329                         crypt = sk_name2crypt(optarg);
330                         break;
331                 case 'd':
332                         datafile = optarg;
333                         break;
334                 case 'e':
335                         expire = atoi(optarg);
336                         if (expire < 60)
337                                 fprintf(stderr, "warning: using a %us key "
338                                         "expiration may cause issues during "
339                                         "key renegotiation\n", expire);
340                         break;
341                 case 'f':
342                         fsname = optarg;
343                         if (strlen(fsname) > MTI_NAME_MAXLEN) {
344                                 fprintf(stderr,
345                                         "error: file system name longer than "
346                                         "%u characters\n", MTI_NAME_MAXLEN);
347                                 return EXIT_FAILURE;
348                         }
349                         break;
350                 case 'g':
351                         mgsnids = optarg;
352                         break;
353                 case 'h':
354                         usage(stdout, argv[0]);
355                         break;
356                 case 'i':
357                         hmac = sk_name2hmac(optarg);
358                         break;
359                 case 'k':
360                         shared_keylen = atoi(optarg);
361                         break;
362                 case 'l':
363                         load = optarg;
364                         break;
365                 case 'm':
366                         modify = optarg;
367                         break;
368                 case 'n':
369                         nodemap = optarg;
370                         if (strlen(nodemap) > LUSTRE_NODEMAP_NAME_LENGTH) {
371                                 fprintf(stderr,
372                                         "error: nodemap name longer than "
373                                         "%u characters\n",
374                                         LUSTRE_NODEMAP_NAME_LENGTH);
375                                 return EXIT_FAILURE;
376                         }
377                         break;
378                 case 'p':
379                         prime_bits = atoi(optarg);
380                         if (prime_bits <= 0) {
381                                 fprintf(stderr,
382                                         "error: invalid prime length: '%s'\n",
383                                         optarg);
384                                 return EXIT_FAILURE;
385                         }
386                         break;
387                 case 'r':
388                         input = optarg;
389                         break;
390                 case 't':
391                         tmp2 = strdup(optarg);
392                         if (!tmp2) {
393                                 fprintf(stderr,
394                                         "error: failed to allocate type\n");
395                                 return EXIT_FAILURE;
396                         }
397                         tmp = strsep(&tmp2, ",");
398                         while (tmp != NULL) {
399                                 if (strcasecmp(tmp, "server") == 0) {
400                                         type |= SK_TYPE_SERVER;
401                                 } else if (strcasecmp(tmp, "mgs") == 0) {
402                                         type |= SK_TYPE_MGS;
403                                 } else if (strcasecmp(tmp, "client") == 0) {
404                                         type |= SK_TYPE_CLIENT;
405                                 } else {
406                                         fprintf(stderr,
407                                                 "error: invalid type '%s', "
408                                                 "must be mgs, server, or client"
409                                                 "\n", optarg);
410                                         return EXIT_FAILURE;
411                                 }
412                                 tmp = strsep(&tmp2, ",");
413                         }
414                         free(tmp2);
415                         break;
416                 case 'v':
417                         verbose++;
418                         break;
419                 case 'w':
420                         output = optarg;
421                         break;
422                 default:
423                         fprintf(stderr, "error: unknown option: '%c'\n", opt);
424                         return EXIT_FAILURE;
425                         break;
426                 }
427         }
428
429         if (optind != argc) {
430                 fprintf(stderr,
431                         "error: extraneous arguments provided, check usage\n");
432                 return EXIT_FAILURE;
433         }
434
435         if (!input && !output && !load && !modify) {
436                 usage(stderr, argv[0]);
437                 return EXIT_FAILURE;
438         }
439
440         /* init gss logger for foreground (no syslog) which prints to stderr */
441         initerr(NULL, verbose, 1);
442
443         if (input)
444                 return print_config(input);
445
446         if (load) {
447                 if (sk_load_keyfile(load))
448                         return EXIT_FAILURE;
449                 return EXIT_SUCCESS;
450         }
451
452         if (crypt == SK_CRYPT_INVALID) {
453                 fprintf(stderr, "error: invalid crypt algorithm specified\n");
454                 return EXIT_FAILURE;
455         }
456         if (hmac == SK_HMAC_INVALID) {
457                 fprintf(stderr, "error: invalid HMAC algorithm specified\n");
458                 return EXIT_FAILURE;
459         }
460
461         if (modify && datafile) {
462                 fprintf(stderr,
463                         "error: data file option not valid in key modify\n");
464                 return EXIT_FAILURE;
465         }
466
467         if (modify) {
468                 config = sk_read_file(modify);
469                 if (!config)
470                         return EXIT_FAILURE;
471
472                 if (type != SK_TYPE_INVALID) {
473                         /* generate key when adding client type */
474                         if (!(config->skc_type & SK_TYPE_CLIENT) &&
475                             type & SK_TYPE_CLIENT)
476                                 generate_prime = true;
477                         else if (!(type & SK_TYPE_CLIENT))
478                                 memset(config->skc_p, 0, SK_MAX_P_BYTES);
479
480                         config->skc_type = type;
481                 }
482                 if (prime_bits != -1) {
483                         memset(config->skc_p, 0, SK_MAX_P_BYTES);
484                         if (config->skc_prime_bits != prime_bits &&
485                             config->skc_type & SK_TYPE_CLIENT)
486                                 generate_prime = true;
487                 }
488         } else {
489                 /* write mode for a new key */
490                 if (!fsname && !mgsnids) {
491                         fprintf(stderr,
492                                 "error: missing --fsname or --mgsnids\n");
493                         return EXIT_FAILURE;
494                 }
495
496                 config = calloc(1, sizeof(*config));
497                 if (!config)
498                         return EXIT_FAILURE;
499
500                 /* Set the defaults for new key */
501                 config->skc_version = SK_CONF_VERSION;
502                 config->skc_expire = SK_DEFAULT_EXPIRE;
503                 config->skc_shared_keylen = SK_DEFAULT_SK_KEYLEN;
504                 config->skc_prime_bits = SK_DEFAULT_PRIME_BITS;
505                 config->skc_crypt_alg = SK_CRYPT_AES256_CTR;
506                 config->skc_hmac_alg = SK_HMAC_SHA256;
507                 for (i = 0; i < MAX_MGSNIDS; i++)
508                         config->skc_mgsnids[i] = LNET_NID_ANY;
509
510                 if (type == SK_TYPE_INVALID) {
511                         fprintf(stderr, "error: no type specified for key\n");
512                         goto error;
513                 }
514                 config->skc_type = type;
515                 generate_prime = type & SK_TYPE_CLIENT;
516
517                 /* SK_DEFAULT_NODEMAP is made to fit in skc_nodemap */
518                 strcpy(config->skc_nodemap, SK_DEFAULT_NODEMAP);
519
520                 if (!datafile)
521                         datafile = "/dev/random";
522         }
523
524         if (crypt != SK_CRYPT_EMPTY)
525                 config->skc_crypt_alg = crypt;
526         if (hmac != SK_HMAC_EMPTY)
527                 config->skc_hmac_alg = hmac;
528         if (expire != -1)
529                 config->skc_expire = expire;
530         if (shared_keylen != -1)
531                 config->skc_shared_keylen = shared_keylen;
532         if (prime_bits != -1)
533                 config->skc_prime_bits = prime_bits;
534         if (fsname)
535                 /* fsname string length was checked when parsing
536                  * command-line options
537                  */
538                 strcpy(config->skc_fsname, fsname);
539         if (nodemap)
540                 /* nodemap string length was checked when parsing
541                  * command-line options
542                  */
543                 strcpy(config->skc_nodemap, nodemap);
544         if (mgsnids && parse_mgsnids(mgsnids, config))
545                 goto error;
546         if (sk_validate_config(config)) {
547                 fprintf(stderr, "error: key configuration failed validation\n");
548                 goto error;
549         }
550
551         if (datafile && get_key_data(datafile, config->skc_shared_key,
552                                      config->skc_shared_keylen)) {
553                 fprintf(stderr, "error: failure getting key data from '%s'\n",
554                         datafile);
555                 goto error;
556         }
557
558         if (generate_prime) {
559                 printf("Generating DH parameters, this can take a while...\n");
560                 dh = DH_generate_parameters(config->skc_prime_bits,
561                                             SK_GENERATOR, NULL, NULL);
562                 if (BN_num_bytes(dh->p) > SK_MAX_P_BYTES) {
563                         fprintf(stderr, "error: cannot generate DH parameters: "
564                                 "requested length %d exceeds maximum %d\n",
565                                 config->skc_prime_bits, SK_MAX_P_BYTES * 8);
566                         goto error;
567                 }
568                 if (BN_bn2bin(dh->p, config->skc_p) != BN_num_bytes(dh->p)) {
569                         fprintf(stderr,
570                                 "error: convert BIGNUM p to binary failed\n");
571                         goto error;
572                 }
573         }
574
575         if (write_config_file(modify ?: output, config, modify))
576                 goto error;
577
578         return EXIT_SUCCESS;
579
580 error:
581         free(config);
582         return EXIT_FAILURE;
583 }