Whamcloud - gitweb
LU-8901 misc: update Intel copyright messages for 2016
[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 <errno.h>
31 #include <fcntl.h>
32 #include <getopt.h>
33 #include <limits.h>
34 #include <stdarg.h>
35 #include <stdbool.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <stdlib.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <unistd.h>
42 #include <lnet/nidstr.h>
43 #include <lustre/lustre_idl.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_opt[] = {
338                 {"crypt", 1, 0, 'c'},
339                 {"data", 1, 0, 'd'},
340                 {"expire", 1, 0, 'e'},
341                 {"fsname", 1, 0, 'f'},
342                 {"mgsnids", 1, 0, 'g'},
343                 {"help", 0, 0, 'h'},
344                 {"hmac", 1, 0, 'i'},
345                 {"integrity", 1, 0, 'i'},
346                 {"key-bits", 1, 0, 'k'},
347                 {"shared", 1, 0, 'k'},
348                 {"load", 1, 0, 'l'},
349                 {"modify", 1, 0, 'm'},
350                 {"nodemap", 1, 0, 'n'},
351                 {"prime-bits", 1, 0, 'p'},
352                 {"read", 1, 0, 'r'},
353                 {"type", 1, 0, 't'},
354                 {"verbose", 0, 0, 'v'},
355                 {"write", 1, 0, 'w'},
356                 {0, 0, 0, 0},
357         };
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_opt,
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 }