Whamcloud - gitweb
LU-3289 gss: Add userspace support for GSS null and 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  * Author: Jeremy Filizetti <jfilizet@iu.edu>
26  */
27
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <getopt.h>
31 #include <limits.h>
32 #include <stdarg.h>
33 #include <stdbool.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <stdlib.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <unistd.h>
40 #include <lnet/nidstr.h>
41 #include <lustre/lustre_idl.h>
42
43 #include "sk_utils.h"
44 #include "err_util.h"
45
46 #ifndef _GNU_SOURCE
47 #define _GNU_SOURCE
48 #endif
49
50 /* One week default expiration */
51 #define SK_DEFAULT_EXPIRE 604800
52 #define SK_DEFAULT_SK_KEYLEN 256
53 #define SK_DEFAULT_DH_KEYLEN 1024
54 #define SK_DEFAULT_NODEMAP "default"
55
56 /* Names match up with openssl enc and dgst commands */
57 char *sk_crypt2name[] = {
58         [SK_CRYPT_EMPTY] = "NONE",
59         [SK_CRYPT_AES256_CTR] = "AES-256-CTR",
60 };
61
62 char *sk_hmac2name[] = {
63         [SK_HMAC_EMPTY] = "NONE",
64         [SK_HMAC_SHA256] = "SHA256",
65         [SK_HMAC_SHA512] = "SHA512",
66 };
67
68 int sk_name2crypt(char *name)
69 {
70         int i;
71
72         for (i = 0; i < SK_CRYPT_MAX; i++) {
73                 if (strcasecmp(name, sk_crypt2name[i]) == 0)
74                         return i;
75         }
76
77         return SK_CRYPT_INVALID;
78 }
79
80 int sk_name2hmac(char *name)
81 {
82         int i;
83
84         for (i = 0; i < SK_HMAC_MAX; i++) {
85                 if (strcasecmp(name, sk_hmac2name[i]) == 0)
86                         return i;
87         }
88
89         return SK_HMAC_INVALID;
90 }
91
92 void usage(FILE *fp, char *program)
93 {
94         int i;
95
96         fprintf(fp, "Usage %s [OPTIONS] -l <file> | -m <file> | "
97                 "-r <file> | -w <file>\n", program);
98         fprintf(fp, "-l|--load    <file>        Load key from file into user's "
99                 "session keyring\n");
100         fprintf(fp, "-m|--modify  <file>        Modify a file's key "
101                 "attributes\n");
102         fprintf(fp, "-r|--read    <file>        Show file's key attributes\n");
103         fprintf(fp, "-w|--write   <file>        Generate key file\n\n");
104         fprintf(fp, "Load Options:\n");
105         fprintf(fp, "-t|--type    <type>        Key type (mgs, server, "
106                 "client)\n\n");
107         fprintf(fp, "Modify/Write Options:\n");
108         fprintf(fp, "-c|--crypt   <num> Cipher for encryption "
109                 "(Default: AES Counter mode)\n");
110         for (i = 0; i < SK_CRYPT_MAX; i++)
111                 fprintf(fp, "                        %s\n", sk_crypt2name[i]);
112
113         fprintf(fp, "-h|--hmac    <num> Hash alg for HMAC "
114                 "(Default: SHA256)\n");
115         for (i = 0; i < SK_HMAC_MAX; i++)
116                 fprintf(fp, "                        %s\n", sk_hmac2name[i]);
117
118         fprintf(fp, "-e|--expire  <num> Seconds before contexts from "
119                 "key expire (Default: %d seconds)\n", SK_DEFAULT_EXPIRE);
120         fprintf(fp, "-f|--fsname  <name>        File system name for key\n");
121         fprintf(fp, "-g|--mgsnids <nids>        Comma seperated list of MGS "
122                 "NIDs.  Only required when mgssec is used (Default: "
123                 "\"\")\n");
124         fprintf(fp, "-n|--nodemap <name>        Nodemap name for key "
125                 "(Default: \"%s\")\n", SK_DEFAULT_NODEMAP);
126         fprintf(fp, "-s|--session <len> DHKE Public key length in bits "
127                 "(Default: %d)\n", SK_DEFAULT_DH_KEYLEN);
128         fprintf(fp, "-k|--shared  <len> Shared key length in bits "
129                 "(Default: %d)\n", SK_DEFAULT_SK_KEYLEN);
130         fprintf(fp, "-d|--data    <file>        Shared key data source "
131                 "(Default: /dev/random)\n\n");
132         fprintf(fp, "Other Options:\n");
133         fprintf(fp, "-v|--verbose           Increase verbosity for "
134                 "errors\n");
135         exit(EXIT_FAILURE);
136 }
137
138 ssize_t get_key_data(char *src, void *buffer, size_t bits)
139 {
140         char *ptr = buffer;
141         size_t remain;
142         ssize_t rc;
143         int fd;
144
145         /* convert bits to minimum number of bytes */
146         remain = (bits + 7) / 8;
147
148         fd = open(src, O_RDONLY);
149         if (fd < 0) {
150                 fprintf(stderr, "Failed to open %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, "Key source too short for key size\n");
167                         rc = -ENODATA;
168                         goto out;
169                 }
170                 ptr += rc;
171                 remain -= rc;
172         }
173         rc = 0;
174
175 out:
176         close(fd);
177         return rc;
178 }
179
180 int write_config_file(char *output_file, struct sk_keyfile_config *config,
181                       bool overwrite)
182 {
183         size_t rc;
184         int fd;
185         int flags = O_WRONLY | O_CREAT;
186
187         if (!overwrite)
188                 flags |= O_EXCL;
189
190         sk_config_cpu_to_disk(config);
191
192         fd = open(output_file, flags, 0400);
193         if (fd < 0) {
194                 fprintf(stderr, "Failed to open %s: %s\n", output_file,
195                         strerror(errno));
196                 return -errno;
197         }
198
199         rc = write(fd, config, sizeof(*config));
200         if (rc < 0) {
201                 fprintf(stderr, "Error writing to %s: %s\n", output_file,
202                         strerror(errno));
203                 rc = -errno;
204
205         } else if (rc != sizeof(*config)) {
206                 fprintf(stderr, "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 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, "Key configuration failed validation\n");
228                 free(config);
229                 return EXIT_FAILURE;
230         }
231
232         printf("Version:        %u\n", config->skc_version);
233         printf("HMAC alg:       %s\n", sk_hmac2name[config->skc_hmac_alg]);
234         printf("Crypt alg:      %s\n", sk_crypt2name[config->skc_crypt_alg]);
235         printf("Ctx Expiration: %u seconds\n", config->skc_expire);
236         printf("Shared keylen:  %u bits\n", config->skc_shared_keylen);
237         printf("Session keylen: %u bits\n", config->skc_session_keylen);
238         printf("File system:    %s\n", config->skc_fsname);
239         printf("MGS NIDs:       ");
240         for (i = 0; i < MAX_MGSNIDS; i++) {
241                 if (config->skc_mgsnids[i] == LNET_NID_ANY)
242                         continue;
243                 printf("%s ", libcfs_nid2str(config->skc_mgsnids[i]));
244         }
245         printf("\n");
246         printf("Nodemap name:   %s\n", config->skc_nodemap);
247         printf("Shared key:\n");
248         print_hex(0, config->skc_shared_key, config->skc_shared_keylen / 8);
249
250         free(config);
251         return EXIT_SUCCESS;
252 }
253
254 int parse_mgsnids(char *mgsnids, struct sk_keyfile_config *config)
255 {
256         lnet_nid_t nid;
257         char *ptr;
258         char *sep;
259         char *end;
260         int rc = 0;
261         int i;
262
263         /* replace all old values */
264         for (i = 0; i < MAX_MGSNIDS; i++)
265                 config->skc_mgsnids[i] = LNET_NID_ANY;
266
267         i = 0;
268         end = mgsnids + strlen(mgsnids);
269         ptr = mgsnids;
270         while (ptr < end && i < MAX_MGSNIDS) {
271                 sep = strstr(ptr, ",");
272                 if (sep != NULL)
273                         *sep = '\0';
274
275                 nid = libcfs_str2nid(ptr);
276                 if (nid == LNET_NID_ANY) {
277                         fprintf(stderr, "Invalid MGS NID: %s\n", ptr);
278                         rc = -EINVAL;
279                         break;
280                 }
281
282                 config->skc_mgsnids[i++] = nid;
283                 ptr += strlen(ptr) + 1;
284         }
285
286         if (i == MAX_MGSNIDS) {
287                 fprintf(stderr, "Too many MGS NIDs provided\n");
288                 rc = -E2BIG;
289         }
290
291         return rc;
292 }
293
294 int main(int argc, char **argv)
295 {
296         struct sk_keyfile_config *config;
297         char *data = NULL;
298         char *input = NULL;
299         char *load = NULL;
300         char *modify = NULL;
301         char *output = NULL;
302         char *mgsnids = NULL;
303         char *nodemap = NULL;
304         char *fsname = NULL;
305         int type = SK_TYPE_INVALID;
306         int crypt = SK_CRYPT_EMPTY;
307         int hmac = SK_HMAC_EMPTY;
308         int expire = -1;
309         int shared_keylen = -1;
310         int session_keylen = -1;
311         int verbose = 0;
312         int i;
313         int opt;
314
315         static struct option long_opt[] = {
316                 {"crypt", 1, 0, 'c'},
317                 {"data", 1, 0, 'd'},
318                 {"expire", 1, 0, 'e'},
319                 {"fsname", 1, 0, 'f'},
320                 {"mgsnids", 1, 0, 'g'},
321                 {"hmac", 1, 0, 'h'},
322                 {"load", 1, 0, 'l'},
323                 {"modify", 1, 0, 'm'},
324                 {"nodemap", 1, 0, 'n'},
325                 {"read", 1, 0, 'r'},
326                 {"session", 1, 0, 's'},
327                 {"shared", 1, 0, 'k'},
328                 {"type", 1, 0, 't'},
329                 {"write", 1, 0, 'w'},
330                 {"verbose", 0, 0, 'v'},
331                 {"help", 0, 0, 'p'},
332                 {0, 0, 0, 0},
333         };
334
335         while ((opt = getopt_long(argc, argv, "c:d:e:f:g:h:l:m:n:pr:s:k:t:w:v",
336                                   long_opt, NULL)) != EOF) {
337                 switch (opt) {
338                 case 'c':
339                         crypt = sk_name2crypt(optarg);
340                         break;
341                 case 'd':
342                         data = optarg;
343                         break;
344                 case 'e':
345                         expire = atoi(optarg);
346                         if (expire < 60)
347                                 fprintf(stderr, "WARNING: Using a short key "
348                                         "expiration may cause issues during "
349                                         "key renegotiation\n");
350                         break;
351                 case 'f':
352                         fsname = optarg;
353                         if (strlen(fsname) > MTI_NAME_MAXLEN) {
354                                 fprintf(stderr, "File system name too long\n");
355                                 return EXIT_FAILURE;
356                         }
357                         break;
358                 case 'g':
359                         mgsnids = optarg;
360                         break;
361                 case 'h':
362                         hmac = sk_name2hmac(optarg);
363                         break;
364                 case 'l':
365                         load = optarg;
366                         break;
367                 case 'n':
368                         nodemap = optarg;
369                         if (strlen(nodemap) > LUSTRE_NODEMAP_NAME_LENGTH) {
370                                 fprintf(stderr, "Nodemap name too long\n");
371                                 return EXIT_FAILURE;
372                         }
373                         break;
374                 case 'm':
375                         modify = optarg;
376                         break;
377                 case 'p':
378                         usage(stdout, argv[0]);
379                         break;
380                 case 'r':
381                         input = optarg;
382                         break;
383                 case 's':
384                         session_keylen = atoi(optarg);
385                         break;
386                 case 'k':
387                         shared_keylen = atoi(optarg);
388                         break;
389                 case 't':
390                         if (!strcasecmp(optarg, "server")) {
391                                 type = SK_TYPE_SERVER;
392                         } else if (!strcasecmp(optarg, "mgs")) {
393                                 type = SK_TYPE_MGS;
394                         } else if (!strcasecmp(optarg, "client")) {
395                                 type = SK_TYPE_CLIENT;
396                         } else {
397                                 fprintf(stderr, "type must be mgs, server, or "
398                                         "client\n");
399                                 return EXIT_FAILURE;
400                         }
401                         break;
402                 case 'w':
403                         output = optarg;
404                         break;
405                 case 'v':
406                         verbose++;
407                         break;
408                 default:
409                         fprintf(stderr, "Unknown option: %c\n", opt);
410                         return EXIT_FAILURE;
411                         break;
412                 }
413         }
414
415         if (optind != argc) {
416                 fprintf(stderr, "Extraneous arguments provided, check usage\n");
417                 return EXIT_FAILURE;
418         }
419
420         if (!input && !output && !load && !modify) {
421                 usage(stderr, argv[0]);
422                 return EXIT_FAILURE;
423         }
424
425         /* init gss logger for foreground (no syslog) which prints to stderr */
426         initerr(NULL, verbose, 1);
427
428         if (input)
429                 return print_config(input);
430
431         if (load) {
432                 if (type == SK_TYPE_INVALID) {
433                         fprintf(stderr, "type must be specified when loading "
434                                 "a key\n");
435                         return EXIT_FAILURE;
436                 }
437
438                 if (sk_load_keyfile(load, type))
439                         return EXIT_FAILURE;
440                 return EXIT_SUCCESS;
441         }
442
443         if (crypt == SK_CRYPT_INVALID) {
444                 fprintf(stderr, "Invalid crypt algorithm specified\n");
445                 return EXIT_FAILURE;
446         }
447         if (hmac == SK_HMAC_INVALID) {
448                 fprintf(stderr, "Invalid HMAC algorithm specified\n");
449                 return EXIT_FAILURE;
450         }
451
452         if (modify) {
453                 config = sk_read_file(modify);
454                 if (!config)
455                         return EXIT_FAILURE;
456
457                 if (crypt != SK_CRYPT_EMPTY)
458                         config->skc_crypt_alg = crypt;
459                 if (hmac != SK_HMAC_EMPTY)
460                         config->skc_hmac_alg = hmac;
461                 if (expire != -1)
462                         config->skc_expire = expire;
463                 if (shared_keylen != -1)
464                         config->skc_shared_keylen = shared_keylen;
465                 if (session_keylen != -1)
466                         config->skc_session_keylen = session_keylen;
467                 if (fsname)
468                         strncpy(config->skc_fsname, fsname, strlen(fsname));
469                 if (nodemap)
470                         strncpy(config->skc_nodemap, nodemap, strlen(nodemap));
471                 if (mgsnids && parse_mgsnids(mgsnids, config))
472                         goto error;
473                 if (data && get_key_data(data, config->skc_shared_key,
474                     config->skc_shared_keylen)) {
475                         fprintf(stderr, "Failure getting data for key\n");
476                         goto error;
477                 }
478
479                 if (sk_validate_config(config)) {
480                         fprintf(stderr, "Key configuration failed "
481                                 "validation\n");
482                         goto error;
483                 }
484
485                 if (write_config_file(modify, config, true))
486                         goto error;
487
488                 return EXIT_SUCCESS;
489         }
490
491         /* write mode for a new key */
492         if (!fsname && !mgsnids) {
493                 fprintf(stderr, "Must provide --fsname, "
494                         "--mgsnids, or both\n");
495                 return EXIT_FAILURE;
496         }
497
498         config = malloc(sizeof(*config));
499         if (!config)
500                 return EXIT_FAILURE;
501
502         /* Set the defaults */
503         memset(config, 0, sizeof(*config));
504         config->skc_version = SK_CONF_VERSION;
505         config->skc_expire = SK_DEFAULT_EXPIRE;
506         config->skc_shared_keylen = SK_DEFAULT_SK_KEYLEN;
507         config->skc_session_keylen = SK_DEFAULT_DH_KEYLEN;
508         config->skc_crypt_alg = SK_CRYPT_AES256_CTR;
509         config->skc_hmac_alg = SK_HMAC_SHA256;
510         for (i = 0; i < MAX_MGSNIDS; i++)
511                 config->skc_mgsnids[i] = LNET_NID_ANY;
512
513         if (crypt != SK_CRYPT_EMPTY)
514                 config->skc_crypt_alg = crypt;
515         if (hmac != SK_HMAC_EMPTY)
516                 config->skc_hmac_alg = hmac;
517         if (expire != -1)
518                 config->skc_expire = expire;
519         if (shared_keylen != -1)
520                 config->skc_shared_keylen = shared_keylen;
521         if (session_keylen != -1)
522                 config->skc_session_keylen = session_keylen;
523         if (fsname)
524                 strncpy(config->skc_fsname, fsname, strlen(fsname));
525         if (nodemap)
526                 strncpy(config->skc_nodemap, nodemap, strlen(nodemap));
527         else
528                 strncpy(config->skc_nodemap, SK_DEFAULT_NODEMAP,
529                         strlen(SK_DEFAULT_NODEMAP));
530
531         if (mgsnids && parse_mgsnids(mgsnids, config))
532                 goto error;
533         if (!data)
534                 data = "/dev/random";
535         if (get_key_data(data, config->skc_shared_key,
536             config->skc_shared_keylen)) {
537                 fprintf(stderr, "Failure getting data for key\n");
538                 goto error;
539         }
540
541         if (sk_validate_config(config)) {
542                 fprintf(stderr, "Key configuration failed validation\n");
543                 goto error;
544         }
545
546         if (write_config_file(output, config, false))
547                 goto error;
548
549         return EXIT_SUCCESS;
550
551 error:
552         if (config)
553                 free(config);
554         return EXIT_FAILURE;
555 }