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