Whamcloud - gitweb
LU-3289 gss: Fix issues with SK privacy and integrity mode
[fs/lustre-release.git] / lustre / utils / gss / sk_utils.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 <fcntl.h>
29 #include <limits.h>
30 #include <math.h>
31 #include <string.h>
32 #include <stdbool.h>
33 #include <unistd.h>
34 #include <openssl/dh.h>
35 #include <openssl/engine.h>
36 #include <openssl/err.h>
37 #include <openssl/hmac.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <lnet/nidstr.h>
41
42 #include "sk_utils.h"
43 #include "write_bytes.h"
44
45 #define SK_PBKDF2_ITERATIONS 10000
46
47 static struct sk_crypt_type sk_crypt_types[] = {
48         [SK_CRYPT_AES256_CTR] = {
49                 .sct_name = "ctr(aes)",
50                 .sct_bytes = 32,
51         },
52 };
53
54 static struct sk_hmac_type sk_hmac_types[] = {
55         [SK_HMAC_SHA256] = {
56                 .sht_name = "hmac(sha256)",
57                 .sht_bytes = 32,
58         },
59         [SK_HMAC_SHA512] = {
60                 .sht_name = "hmac(sha512)",
61                 .sht_bytes = 64,
62         },
63 };
64
65 #ifdef _NEW_BUILD_
66 # include "lgss_utils.h"
67 #else
68 # include "gss_util.h"
69 # include "gss_oids.h"
70 # include "err_util.h"
71 #endif
72
73 #ifdef _ERR_UTIL_H_
74 /**
75  * Initializes logging
76  * \param[in]   program         Program name to output
77  * \param[in]   verbose         Verbose flag
78  * \param[in]   fg              Whether or not to run in foreground
79  *
80  */
81 void sk_init_logging(char *program, int verbose, int fg)
82 {
83         initerr(program, verbose, fg);
84 }
85 #endif
86
87 /**
88  * Loads the key from \a filename and returns the struct sk_keyfile_config.
89  * It should be freed by the caller.
90  *
91  * \param[in]   filename                Disk or key payload data
92  *
93  * \return      sk_keyfile_config       sucess
94  * \return      NULL                    failure
95  */
96 struct sk_keyfile_config *sk_read_file(char *filename)
97 {
98         struct sk_keyfile_config *config;
99         char *ptr;
100         size_t rc;
101         size_t remain;
102         int fd;
103
104         config = malloc(sizeof(*config));
105         if (!config) {
106                 printerr(0, "Failed to allocate memory for config\n");
107                 return NULL;
108         }
109
110         /* allow standard input override */
111         if (strcmp(filename, "-") == 0)
112                 fd = dup(STDIN_FILENO);
113         else
114                 fd = open(filename, O_RDONLY);
115
116         if (fd == -1) {
117                 printerr(0, "Error opening file %s: %s\n", filename,
118                          strerror(errno));
119                 goto out_free;
120         }
121
122         ptr = (char *)config;
123         remain = sizeof(*config);
124         while (remain > 0) {
125                 rc = read(fd, ptr, remain);
126                 if (rc == -1) {
127                         if (errno == EINTR)
128                                 continue;
129                         printerr(0, "read() failed on %s: %s\n", filename,
130                                  strerror(errno));
131                         goto out_close;
132                 } else if (rc == 0) {
133                         printerr(0, "File %s does not have a complete key\n",
134                                  filename);
135                         goto out_close;
136                 }
137                 ptr += rc;
138                 remain -= rc;
139         }
140
141         close(fd);
142         sk_config_disk_to_cpu(config);
143         return config;
144
145 out_close:
146         close(fd);
147 out_free:
148         free(config);
149         return NULL;
150 }
151
152 /**
153  * Checks if a key matching \a description is found in the keyring for
154  * logging purposes and then attempts to load \a payload of \a psize into a key
155  * with \a description.
156  *
157  * \param[in]   payload         Key payload
158  * \param[in]   psize           Payload size
159  * \param[in]   description     Description used for key in keyring
160  *
161  * \return      0       sucess
162  * \return      -1      failure
163  */
164 static key_serial_t sk_load_key(const struct sk_keyfile_config *skc,
165                                 const char *description)
166 {
167         struct sk_keyfile_config payload;
168         key_serial_t key;
169
170         memcpy(&payload, skc, sizeof(*skc));
171
172         /* In the keyring use the disk layout so keyctl pipe can be used */
173         sk_config_cpu_to_disk(&payload);
174
175         /* Check to see if a key is already loaded matching description */
176         key = keyctl_search(KEY_SPEC_USER_KEYRING, "user", description, 0);
177         if (key != -1)
178                 printerr(2, "Key %d found in session keyring, replacing\n",
179                          key);
180
181         key = add_key("user", description, &payload, sizeof(payload),
182                       KEY_SPEC_USER_KEYRING);
183         if (key != -1)
184                 printerr(2, "Added key %d with description %s\n", key,
185                          description);
186         else
187                 printerr(0, "Failed to add key with %s\n", description);
188
189         return key;
190 }
191
192 /**
193  * Reads the key from \a path, verifies it and loads into the session keyring
194  * using a description determined by the the \a type.  Existing keys with the
195  * same description are replaced.
196  *
197  * \param[in]   path    Path to key file
198  * \param[in]   type    Type of key to load which determines the description
199  *
200  * \return      0       sucess
201  * \return      -1      failure
202  */
203 int sk_load_keyfile(char *path, int type)
204 {
205         struct sk_keyfile_config *config;
206         char description[SK_DESCRIPTION_SIZE + 1];
207         struct stat buf;
208         int i;
209         int rc;
210         int rc2 = -1;
211
212         rc = stat(path, &buf);
213         if (rc == -1) {
214                 printerr(0, "stat() failed for file %s: %s\n", path,
215                          strerror(errno));
216                 return rc2;
217         }
218
219         config = sk_read_file(path);
220         if (!config)
221                 return rc2;
222
223         /* Similar to ssh, require adequate care of key files */
224         if (buf.st_mode & (S_IRGRP | S_IWGRP | S_IWOTH | S_IXOTH)) {
225                 printerr(0, "Shared key files must be read/writeable only by "
226                          "owner\n");
227                 return -1;
228         }
229
230         if (sk_validate_config(config))
231                 goto out;
232
233         /* The server side can have multiple key files per file system so
234          * the nodemap name is appended to the key description to uniquely
235          * identify it */
236         if (type & SK_TYPE_MGS) {
237                 /* Any key can be an MGS key as long as we are told to use it */
238                 rc = snprintf(description, SK_DESCRIPTION_SIZE, "lustre:MGS:%s",
239                               config->skc_nodemap);
240                 if (rc >= SK_DESCRIPTION_SIZE)
241                         goto out;
242                 if (sk_load_key(config, description) == -1)
243                         goto out;
244         }
245         if (type & SK_TYPE_SERVER) {
246                 /* Server keys need to have the file system name in the key */
247                 if (!config->skc_fsname) {
248                         printerr(0, "Key configuration has no file system "
249                                  "attribute.  Can't load as server type\n");
250                         goto out;
251                 }
252                 rc = snprintf(description, SK_DESCRIPTION_SIZE, "lustre:%s:%s",
253                               config->skc_fsname, config->skc_nodemap);
254                 if (rc >= SK_DESCRIPTION_SIZE)
255                         goto out;
256                 if (sk_load_key(config, description) == -1)
257                         goto out;
258         }
259         if (type & SK_TYPE_CLIENT) {
260                 /* Load client file system key */
261                 if (config->skc_fsname) {
262                         rc = snprintf(description, SK_DESCRIPTION_SIZE,
263                                       "lustre:%s", config->skc_fsname);
264                         if (rc >= SK_DESCRIPTION_SIZE)
265                                 goto out;
266                         if (sk_load_key(config, description) == -1)
267                                 goto out;
268                 }
269
270                 /* Load client MGC keys */
271                 for (i = 0; i < MAX_MGSNIDS; i++) {
272                         if (config->skc_mgsnids[i] == LNET_NID_ANY)
273                                 continue;
274                         rc = snprintf(description, SK_DESCRIPTION_SIZE,
275                                       "lustre:MGC%s",
276                                       libcfs_nid2str(config->skc_mgsnids[i]));
277                         if (rc >= SK_DESCRIPTION_SIZE)
278                                 goto out;
279                         if (sk_load_key(config, description) == -1)
280                                 goto out;
281                 }
282         }
283
284         rc2 = 0;
285
286 out:
287         free(config);
288         return rc2;
289 }
290
291 /**
292  * Byte swaps config from cpu format to disk
293  *
294  * \param[in,out]       config          sk_keyfile_config to swap
295  */
296 void sk_config_cpu_to_disk(struct sk_keyfile_config *config)
297 {
298         int i;
299
300         if (!config)
301                 return;
302
303         config->skc_version = htobe32(config->skc_version);
304         config->skc_hmac_alg = htobe16(config->skc_hmac_alg);
305         config->skc_crypt_alg = htobe16(config->skc_crypt_alg);
306         config->skc_expire = htobe32(config->skc_expire);
307         config->skc_shared_keylen = htobe32(config->skc_shared_keylen);
308         config->skc_session_keylen = htobe32(config->skc_session_keylen);
309
310         for (i = 0; i < MAX_MGSNIDS; i++)
311                 config->skc_mgsnids[i] = htobe64(config->skc_mgsnids[i]);
312
313         return;
314 }
315
316 /**
317  * Byte swaps config from disk format to cpu
318  *
319  * \param[in,out]       config          sk_keyfile_config to swap
320  */
321 void sk_config_disk_to_cpu(struct sk_keyfile_config *config)
322 {
323         int i;
324
325         if (!config)
326                 return;
327
328         config->skc_version = be32toh(config->skc_version);
329         config->skc_hmac_alg = be16toh(config->skc_hmac_alg);
330         config->skc_crypt_alg = be16toh(config->skc_crypt_alg);
331         config->skc_expire = be32toh(config->skc_expire);
332         config->skc_shared_keylen = be32toh(config->skc_shared_keylen);
333         config->skc_session_keylen = be32toh(config->skc_session_keylen);
334
335         for (i = 0; i < MAX_MGSNIDS; i++)
336                 config->skc_mgsnids[i] = be64toh(config->skc_mgsnids[i]);
337
338         return;
339 }
340
341 /**
342  * Verifies the on key payload format is valid
343  *
344  * \param[in]   config          sk_keyfile_config
345  *
346  * \return      -1      failure
347  * \return      0       success
348  */
349 int sk_validate_config(const struct sk_keyfile_config *config)
350 {
351         int i;
352
353         if (!config) {
354                 printerr(0, "Null configuration passed\n");
355                 return -1;
356         }
357         if (config->skc_version != SK_CONF_VERSION) {
358                 printerr(0, "Invalid version\n");
359                 return -1;
360         }
361         if (config->skc_hmac_alg >= SK_HMAC_MAX) {
362                 printerr(0, "Invalid HMAC algorithm\n");
363                 return -1;
364         }
365         if (config->skc_crypt_alg >= SK_CRYPT_MAX) {
366                 printerr(0, "Invalid crypt algorithm\n");
367                 return -1;
368         }
369         if (config->skc_expire < 60 || config->skc_expire > INT_MAX) {
370                 /* Try to limit key expiration to some reasonable minimum and
371                  * also prevent values over INT_MAX because there appears
372                  * to be a type conversion issue */
373                 printerr(0, "Invalid expiration time should be between %d "
374                          "and %d\n", 60, INT_MAX);
375                 return -1;
376         }
377         if (config->skc_session_keylen % 8 != 0 ||
378             config->skc_session_keylen > SK_SESSION_MAX_KEYLEN_BYTES * 8) {
379                 printerr(0, "Invalid session key length must be a multiple of 8"
380                          " and less then %d bits\n",
381                          SK_SESSION_MAX_KEYLEN_BYTES * 8);
382                 return -1;
383         }
384         if (config->skc_shared_keylen % 8 != 0 ||
385             config->skc_shared_keylen > SK_MAX_KEYLEN_BYTES * 8){
386                 printerr(0, "Invalid shared key max length must be a multiple "
387                          "of 8 and less then %d bits\n",
388                          SK_MAX_KEYLEN_BYTES * 8);
389                 return -1;
390         }
391
392         /* Check for terminating nulls on strings */
393         for (i = 0; i < sizeof(config->skc_fsname) &&
394              config->skc_fsname[i] != '\0';  i++)
395                 ; /* empty loop */
396         if (i == sizeof(config->skc_fsname)) {
397                 printerr(0, "File system name not null terminated\n");
398                 return -1;
399         }
400
401         for (i = 0; i < sizeof(config->skc_nodemap) &&
402              config->skc_nodemap[i] != '\0';  i++)
403                 ; /* empty loop */
404         if (i == sizeof(config->skc_nodemap)) {
405                 printerr(0, "Nodemap name not null terminated\n");
406                 return -1;
407         }
408
409         return 0;
410 }
411
412 /**
413  * Hashes \a string and places the hash in \a hash
414  * at \a hash
415  *
416  * \param[in]           string          Null terminated string to hash
417  * \param[in]           hash_alg        OpenSSL EVP_MD to use for hash
418  * \param[in,out]       hash            gss_buffer_desc to hold the result
419  *
420  * \return      -1      failure
421  * \return      0       success
422  */
423 static int sk_hash_string(const char *string, const EVP_MD *hash_alg,
424                           gss_buffer_desc *hash)
425 {
426         EVP_MD_CTX *ctx = EVP_MD_CTX_create();
427         size_t len = strlen(string);
428         unsigned int hashlen;
429
430         if (!hash->value || hash->length < EVP_MD_size(hash_alg))
431                 goto out_err;
432         if (!EVP_DigestInit_ex(ctx, hash_alg, NULL))
433                 goto out_err;
434         if (!EVP_DigestUpdate(ctx, string, len))
435                 goto out_err;
436         if (!EVP_DigestFinal_ex(ctx, hash->value, &hashlen))
437                 goto out_err;
438
439         EVP_MD_CTX_destroy(ctx);
440         hash->length = hashlen;
441         return 0;
442
443 out_err:
444         EVP_MD_CTX_destroy(ctx);
445         return -1;
446 }
447
448 /**
449  * Hashes \a string and verifies the resulting hash matches the value
450  * in \a current_hash
451  *
452  * \param[in]           string          Null terminated string to hash
453  * \param[in]           hash_alg        OpenSSL EVP_MD to use for hash
454  * \param[in,out]       current_hash    gss_buffer_desc to compare to
455  *
456  * \return      gss error       failure
457  * \return      GSS_S_COMPLETE  success
458  */
459 uint32_t sk_verify_hash(const char *string, const EVP_MD *hash_alg,
460                         const gss_buffer_desc *current_hash)
461 {
462         gss_buffer_desc hash;
463         unsigned char hashbuf[EVP_MAX_MD_SIZE];
464
465         hash.value = hashbuf;
466         hash.length = sizeof(hashbuf);
467
468         if (sk_hash_string(string, hash_alg, &hash))
469                 return GSS_S_FAILURE;
470         if (current_hash->length != hash.length)
471                 return GSS_S_DEFECTIVE_TOKEN;
472         if (memcmp(current_hash->value, hash.value, hash.length))
473                 return GSS_S_BAD_SIG;
474
475         return GSS_S_COMPLETE;
476 }
477
478 static inline int sk_config_has_mgsnid(struct sk_keyfile_config *config,
479                                        const char *mgsnid)
480 {
481         lnet_nid_t nid;
482         int i;
483
484         nid = libcfs_str2nid(mgsnid);
485         if (nid == LNET_NID_ANY)
486                 return 0;
487
488         for (i = 0; i < MAX_MGSNIDS; i++)
489                 if  (config->skc_mgsnids[i] == nid)
490                         return 1;
491         return 0;
492 }
493
494 /**
495  * Create an sk_cred structure populated with initial configuration info and the
496  * key.  \a tgt and \a nodemap are used in determining the expected key
497  * description so the key can be found by searching the keyring.
498  * This is done because there is no easy way to pass keys from the mount command
499  * all the way to the request_key call.  In addition any keys can be dynamically
500  * added to the keyrings and still found.  The keyring that needs to be used
501  * must be the session keyring.
502  *
503  * \param[in]   tgt             Target file system
504  * \param[in]   nodemap         Cluster name for the key.  This correlates to
505  *                              the nodemap name and is used by the server side.
506  *                              For the client this will be NULL.
507  * \param[in]   flags           Flags for the credentials
508  *
509  * \return      sk_cred Allocated struct sk_cred on success
510  * \return      NULL    failure
511  */
512 struct sk_cred *sk_create_cred(const char *tgt, const char *nodemap,
513                                const uint32_t flags)
514 {
515         struct sk_keyfile_config *config;
516         struct sk_kernel_ctx *kctx;
517         struct sk_cred *skc = NULL;
518         char description[SK_DESCRIPTION_SIZE + 1];
519         char fsname[MTI_NAME_MAXLEN + 1];
520         const char *mgsnid = NULL;
521         char *ptr;
522         long sk_key;
523         int keylen;
524         int len;
525         int rc;
526
527         printerr(2, "Creating credentials for target: %s with nodemap: %s\n",
528                  tgt, nodemap);
529
530         memset(description, 0, sizeof(description));
531         memset(fsname, 0, sizeof(fsname));
532
533         /* extract the file system name from target */
534         ptr = index(tgt, '-');
535         if (!ptr) {
536                 len = strlen(tgt);
537
538                 /* This must be an MGC target */
539                 if (strncmp(tgt, "MGC", 3) || len <= 3) {
540                         printerr(0, "Invalid target name\n");
541                         return NULL;
542                 }
543                 mgsnid = tgt + 3;
544         } else {
545                 len = ptr - tgt;
546         }
547
548         if (len > MTI_NAME_MAXLEN) {
549                 printerr(0, "Invalid target name\n");
550                 return NULL;
551         }
552         memcpy(fsname, tgt, len);
553
554         if (nodemap) {
555                 if (mgsnid)
556                         rc = snprintf(description, SK_DESCRIPTION_SIZE,
557                                       "lustre:MGS:%s", nodemap);
558                 else
559                         rc = snprintf(description, SK_DESCRIPTION_SIZE,
560                                       "lustre:%s:%s", fsname, nodemap);
561         } else {
562                 rc = snprintf(description, SK_DESCRIPTION_SIZE, "lustre:%s",
563                               fsname);
564         }
565
566         if (rc >= SK_DESCRIPTION_SIZE) {
567                 printerr(0, "Invalid key description\n");
568                 return NULL;
569         }
570
571         /* It may be a good idea to move Lustre keys to the gss_keyring
572          * (lgssc) type so that they expire when Lustre modules are removed.
573          * Unfortunately it can't be done at mount time because the mount
574          * syscall could trigger the Lustre modules to load and until that
575          * point we don't have a lgssc key type.
576          *
577          * TODO: Query the community for a consensus here  */
578         printerr(2, "Searching for key with description: %s\n", description);
579         sk_key = keyctl_search(KEY_SPEC_USER_KEYRING, "user",
580                                description, 0);
581         if (sk_key == -1) {
582                 printerr(1, "No key found for %s\n", description);
583                 return NULL;
584         }
585
586         keylen = keyctl_read_alloc(sk_key, (void **)&config);
587         if (keylen == -1) {
588                 printerr(0, "keyctl_read() failed for key %ld: %s\n", sk_key,
589                          strerror(errno));
590                 return NULL;
591         } else if (keylen != sizeof(*config)) {
592                 printerr(0, "Unexpected key size: %d returned for key %ld, "
593                          "expected %zu bytes\n",
594                          keylen, sk_key, sizeof(*config));
595                 goto out_err;
596         }
597
598         sk_config_disk_to_cpu(config);
599
600         if (sk_validate_config(config)) {
601                 printerr(0, "Invalid key configuration for key: %ld\n", sk_key);
602                 goto out_err;
603         }
604
605         if (mgsnid && !sk_config_has_mgsnid(config, mgsnid)) {
606                 printerr(0, "Target name does not match key's MGS NIDs\n");
607                 goto out_err;
608         }
609
610         if (!mgsnid && strcmp(fsname, config->skc_fsname)) {
611                 printerr(0, "Target name does not match key's file system\n");
612                 goto out_err;
613         }
614
615         skc = malloc(sizeof(*skc));
616         if (!skc) {
617                 printerr(0, "Failed to allocate memory for sk_cred\n");
618                 goto out_err;
619         }
620
621         /* this initializes all gss_buffer_desc to empty as well */
622         memset(skc, 0, sizeof(*skc));
623
624         skc->sc_flags = flags;
625         skc->sc_tgt.length = strlen(tgt) + 1;
626         skc->sc_tgt.value = malloc(skc->sc_tgt.length);
627         if (!skc->sc_tgt.value) {
628                 printerr(0, "Failed to allocate memory for target\n");
629                 goto out_err;
630         }
631         memcpy(skc->sc_tgt.value, tgt, skc->sc_tgt.length);
632
633         skc->sc_nodemap_hash.length = EVP_MD_size(EVP_sha256());
634         skc->sc_nodemap_hash.value = malloc(skc->sc_nodemap_hash.length);
635         if (!skc->sc_nodemap_hash.value) {
636                 printerr(0, "Failed to allocate memory for nodemap hash\n");
637                 goto out_err;
638         }
639
640         if (sk_hash_string(config->skc_nodemap, EVP_sha256(),
641                            &skc->sc_nodemap_hash)) {
642                 printerr(0, "Failed to generate hash for nodemap name\n");
643                 goto out_err;
644         }
645
646         kctx = &skc->sc_kctx;
647         kctx->skc_version = config->skc_version;
648         kctx->skc_hmac_alg = config->skc_hmac_alg;
649         kctx->skc_crypt_alg = config->skc_crypt_alg;
650         kctx->skc_expire = config->skc_expire;
651
652         /* key payload format is in bits, convert to bytes */
653         skc->sc_session_keylen = config->skc_session_keylen / 8;
654         kctx->skc_shared_key.length = config->skc_shared_keylen / 8;
655         kctx->skc_shared_key.value = malloc(kctx->skc_shared_key.length);
656         if (!kctx->skc_shared_key.value) {
657                 printerr(0, "Failed to allocate memory for shared key\n");
658                 goto out_err;
659         }
660         memcpy(kctx->skc_shared_key.value, config->skc_shared_key,
661                kctx->skc_shared_key.length);
662
663         free(config);
664
665         return skc;
666
667 out_err:
668         sk_free_cred(skc);
669
670         free(config);
671         return NULL;
672 }
673
674 static void sk_free_parameters(struct sk_cred *skc)
675 {
676         if (skc->sc_params)
677                 DH_free(skc->sc_params);
678         if (skc->sc_p.value)
679                 free(skc->sc_p.value);
680         if (skc->sc_pub_key.value)
681                 free(skc->sc_pub_key.value);
682
683         skc->sc_params = NULL;
684         skc->sc_p.value = NULL;
685         skc->sc_p.length = 0;
686         skc->sc_pub_key.value = NULL;
687         skc->sc_pub_key.length = 0;
688 }
689
690 /**
691  * Generates a public key and computes the private key for the DH key exchange.
692  * The parameters must be populated with the p and g from the peer.
693  *
694  * \param[in,out]       skc     Shared key credentials structure to populate
695  *                              with DH parameters
696  *
697  * \retval      GSS_S_COMPLETE  success
698  * \retval      GSS_S_FAILURE   failure
699  */
700 static uint32_t sk_gen_responder_params(struct sk_cred *skc)
701 {
702         int rc;
703
704         /* No keys to generate without privacy mode */
705         if ((skc->sc_flags & LGSS_SVC_PRIV) == 0)
706                 return GSS_S_COMPLETE;
707
708         skc->sc_params = DH_new();
709         if (!skc->sc_params) {
710                 printerr(0, "Failed to allocate DH\n");
711                 return GSS_S_FAILURE;
712         }
713
714         /* responder should already have sc_p populated */
715         skc->sc_params->p = BN_bin2bn(skc->sc_p.value, skc->sc_p.length, NULL);
716         if (!skc->sc_params->p) {
717                 printerr(0, "Failed to convert binary to BIGNUM\n");
718                 return GSS_S_FAILURE;
719         }
720
721         /* and we use a static generator for shared key */
722         skc->sc_params->g = BN_new();
723         if (!skc->sc_params->g) {
724                 printerr(0, "Failed to allocate new BIGNUM\n");
725                 return GSS_S_FAILURE;
726         }
727         if (BN_set_word(skc->sc_params->g, SK_GENERATOR) != 1) {
728                 printerr(0, "Failed to set g value for DH params\n");
729                 return GSS_S_FAILURE;
730         }
731
732         /* verify that we have a safe prime and valid generator */
733         if (DH_check(skc->sc_params, &rc) != 1) {
734                 printerr(0, "DH_check() failed: %d\n", rc);
735                 return GSS_S_FAILURE;
736         } else if (rc) {
737                 printerr(0, "DH_check() returned error codes: 0x%x\n", rc);
738                 return GSS_S_FAILURE;
739         }
740
741         if (DH_generate_key(skc->sc_params) != 1) {
742                 printerr(0, "Failed to generate public DH key: %s\n",
743                          ERR_error_string(ERR_get_error(), NULL));
744                 return GSS_S_FAILURE;
745         }
746
747         skc->sc_pub_key.length = BN_num_bytes(skc->sc_params->pub_key);
748         skc->sc_pub_key.value = malloc(skc->sc_pub_key.length);
749         if (!skc->sc_pub_key.value) {
750                 printerr(0, "Failed to allocate memory for public key\n");
751                 return GSS_S_FAILURE;
752         }
753
754         BN_bn2bin(skc->sc_params->pub_key, skc->sc_pub_key.value);
755
756         return GSS_S_COMPLETE;
757 }
758
759 /**
760  * Generates shared key Diffie Hellman parameters used for the DH key exchange
761  * between host and peer if privacy mode is enabled
762  *
763  * \param[in,out]       skc     Shared key credentials structure to populate
764  *                              with DH parameters
765  *
766  * \retval      GSS_S_COMPLETE  success
767  * \retval      GSS_S_FAILURE   failure
768  */
769 static uint32_t sk_gen_initiator_params(struct sk_cred *skc)
770 {
771         int rc;
772
773         /* The credential could be used so free existing parameters */
774         sk_free_parameters(skc);
775
776         /* Only privacy mode needs the rest of the parameter generation
777          * but we use IV in other modes as well so tokens should be
778          * unique */
779         if ((skc->sc_flags & LGSS_SVC_PRIV) == 0)
780                 return GSS_S_COMPLETE;
781
782         skc->sc_params = DH_generate_parameters(skc->sc_session_keylen * 8,
783                                                 SK_GENERATOR, NULL, NULL);
784         if (skc->sc_params == NULL) {
785                 printerr(0, "Failed to generate diffie-hellman parameters: %s",
786                          ERR_error_string(ERR_get_error(), NULL));
787                 return GSS_S_FAILURE;
788         }
789
790         if (DH_check(skc->sc_params, &rc) != 1) {
791                 printerr(0, "DH_check() failed: %d\n", rc);
792                 return GSS_S_FAILURE;
793         } else if (rc) {
794                 printerr(0, "DH_check() returned error codes: 0x%x\n", rc);
795                 return GSS_S_FAILURE;
796         }
797
798         if (DH_generate_key(skc->sc_params) != 1) {
799                 printerr(0, "Failed to generate public DH key: %s\n",
800                          ERR_error_string(ERR_get_error(), NULL));
801                 return GSS_S_FAILURE;
802         }
803
804         skc->sc_p.length = BN_num_bytes(skc->sc_params->p);
805         skc->sc_pub_key.length = BN_num_bytes(skc->sc_params->pub_key);
806         skc->sc_p.value = malloc(skc->sc_p.length);
807         skc->sc_pub_key.value = malloc(skc->sc_pub_key.length);
808         if (!skc->sc_p.value || !skc->sc_pub_key.value) {
809                 printerr(0, "Failed to allocate memory for params\n");
810                 return GSS_S_FAILURE;
811         }
812
813         BN_bn2bin(skc->sc_params->pub_key, skc->sc_pub_key.value);
814         BN_bn2bin(skc->sc_params->p, skc->sc_p.value);
815
816         return GSS_S_COMPLETE;
817 }
818
819 /**
820  * Generates or populates the DH parameters depending on whether the system is
821  * the initiator or responder for the connection
822  *
823  * \param[in,out]       skc             Shared key credentials structure to
824  *                                      populate with DH parameters
825  * \param[in]           initiator       Boolean whether to initiate parameters
826  *
827  * \retval      GSS_S_COMPLETE  success
828  * \retval      GSS_S_FAILURE   failure
829  */
830 uint32_t sk_gen_params(struct sk_cred *skc, const bool initiator)
831 {
832         uint32_t random;
833
834         /* Random value used by both the request and response as part of the
835          * key binding material.  This also should ensure we have unqiue
836          * tokens that are sent to the remote server which is important because
837          * the token is hashed for the sunrpc cache lookups and a failure there
838          * would cause connection attempts to fail indefinitely due to the large
839          * timeout value on the server side */
840         if (RAND_bytes((unsigned char *)&random, sizeof(random)) != 1) {
841                 printerr(0, "Failed to get data for random parameter\n");
842                 return GSS_S_FAILURE;
843         }
844
845         /* The random value will always be used in byte range operations
846          * so we keep it as big endian from this point on */
847         skc->sc_kctx.skc_host_random = htobe32(random);
848
849         if (initiator)
850                 return sk_gen_initiator_params(skc);
851
852         return sk_gen_responder_params(skc);
853 }
854
855 /**
856  * Convert SK hash algorithm into openssl message digest
857  *
858  * \param[in,out]       alg             SK hash algorithm
859  *
860  * \retval              EVP_MD
861  */
862 static inline const EVP_MD *sk_hash_to_evp_md(enum sk_hmac_alg alg)
863 {
864         switch (alg) {
865         case SK_HMAC_SHA256:
866                 return EVP_sha256();
867         case SK_HMAC_SHA512:
868                 return EVP_sha512();
869         default:
870                 return EVP_md_null();
871         }
872 }
873
874 /**
875  * Signs (via HMAC) the parameters used only in the key initialization protocol.
876  *
877  * \param[in]           key             Key to use for HMAC
878  * \param[in]           bufs            Array of gss_buffer_desc to generate
879  *                                      HMAC for
880  * \param[in]           numbufs         Number of buffers in array
881  * \param[in]           hash_alg        OpenSSL EVP_MD to use for hash
882  * \param[in,out]       hmac            HMAC of buffers is allocated and placed
883  *                                      in this gss_buffer_desc.  Caller must
884  *                                      free this.
885  *
886  * \retval      0       success
887  * \retval      -1      failure
888  */
889 int sk_sign_bufs(gss_buffer_desc *key, gss_buffer_desc *bufs, const int numbufs,
890                  const EVP_MD *hash_alg, gss_buffer_desc *hmac)
891 {
892         HMAC_CTX hctx;
893         unsigned int hashlen = EVP_MD_size(hash_alg);
894         int i;
895         int rc = -1;
896
897         if (hash_alg == EVP_md_null()) {
898                 printerr(0, "Invalid hash algorithm\n");
899                 return -1;
900         }
901
902         HMAC_CTX_init(&hctx);
903
904         hmac->length = hashlen;
905         hmac->value = malloc(hashlen);
906         if (!hmac->value) {
907                 printerr(0, "Failed to allocate memory for HMAC\n");
908                 goto out;
909         }
910
911         if (HMAC_Init_ex(&hctx, key->value, key->length, hash_alg, NULL) != 1) {
912                 printerr(0, "Failed to init HMAC\n");
913                 goto out;
914         }
915
916         for (i = 0; i < numbufs; i++) {
917                 if (HMAC_Update(&hctx, bufs[i].value, bufs[i].length) != 1) {
918                         printerr(0, "Failed to update HMAC\n");
919                         goto out;
920                 }
921         }
922
923         /* The result gets populated in hmac */
924         if (HMAC_Final(&hctx, hmac->value, &hashlen) != 1) {
925                 printerr(0, "Failed to finalize HMAC\n");
926                 goto out;
927         }
928
929         if (hmac->length != hashlen) {
930                 printerr(0, "HMAC size does not match expected\n");
931                 goto out;
932         }
933
934         rc = 0;
935 out:
936         HMAC_CTX_cleanup(&hctx);
937         return rc;
938 }
939
940 /**
941  * Generates an HMAC for gss_buffer_desc array in \a bufs of \a numbufs
942  * and verifies against \a hmac.
943  *
944  * \param[in]   skc             Shared key credentials
945  * \param[in]   bufs            Array of gss_buffer_desc to generate HMAC for
946  * \param[in]   numbufs         Number of buffers in array
947  * \param[in]   hash_alg        OpenSSL EVP_MD to use for hash
948  * \param[in]   hmac            HMAC to verify against
949  *
950  * \retval      GSS_S_COMPLETE  success (match)
951  * \retval      gss error       failure
952  */
953 uint32_t sk_verify_hmac(struct sk_cred *skc, gss_buffer_desc *bufs,
954                         const int numbufs, const EVP_MD *hash_alg,
955                         gss_buffer_desc *hmac)
956 {
957         gss_buffer_desc bufs_hmac;
958         int rc;
959
960         if (sk_sign_bufs(&skc->sc_kctx.skc_shared_key, bufs, numbufs, hash_alg,
961                          &bufs_hmac)) {
962                 printerr(0, "Failed to sign buffers to verify HMAC\n");
963                 if (bufs_hmac.value)
964                         free(bufs_hmac.value);
965                 return GSS_S_FAILURE;
966         }
967
968         if (hmac->length != bufs_hmac.length) {
969                 printerr(0, "Invalid HMAC size\n");
970                 free(bufs_hmac.value);
971                 return GSS_S_BAD_SIG;
972         }
973
974         rc = memcmp(hmac->value, bufs_hmac.value, bufs_hmac.length);
975         free(bufs_hmac.value);
976
977         if (rc)
978                 return GSS_S_BAD_SIG;
979
980         return GSS_S_COMPLETE;
981 }
982
983 /**
984  * Cleanup an sk_cred freeing any resources
985  *
986  * \param[in,out]       skc     Shared key credentials to free
987  */
988 void sk_free_cred(struct sk_cred *skc)
989 {
990         if (!skc)
991                 return;
992
993         if (skc->sc_p.value)
994                 free(skc->sc_p.value);
995         if (skc->sc_pub_key.value)
996                 free(skc->sc_pub_key.value);
997         if (skc->sc_tgt.value)
998                 free(skc->sc_tgt.value);
999         if (skc->sc_nodemap_hash.value)
1000                 free(skc->sc_nodemap_hash.value);
1001         if (skc->sc_hmac.value)
1002                 free(skc->sc_hmac.value);
1003
1004         /* Overwrite keys and IV before freeing */
1005         if (skc->sc_dh_shared_key.value) {
1006                 memset(skc->sc_dh_shared_key.value, 0,
1007                        skc->sc_dh_shared_key.length);
1008                 free(skc->sc_dh_shared_key.value);
1009         }
1010         if (skc->sc_kctx.skc_hmac_key.value) {
1011                 memset(skc->sc_kctx.skc_hmac_key.value, 0,
1012                        skc->sc_kctx.skc_hmac_key.length);
1013                 free(skc->sc_kctx.skc_hmac_key.value);
1014         }
1015         if (skc->sc_kctx.skc_encrypt_key.value) {
1016                 memset(skc->sc_kctx.skc_encrypt_key.value, 0,
1017                        skc->sc_kctx.skc_encrypt_key.length);
1018                 free(skc->sc_kctx.skc_encrypt_key.value);
1019         }
1020         if (skc->sc_kctx.skc_shared_key.value) {
1021                 memset(skc->sc_kctx.skc_shared_key.value, 0,
1022                        skc->sc_kctx.skc_shared_key.length);
1023                 free(skc->sc_kctx.skc_shared_key.value);
1024         }
1025         if (skc->sc_kctx.skc_session_key.value) {
1026                 memset(skc->sc_kctx.skc_session_key.value, 0,
1027                        skc->sc_kctx.skc_session_key.length);
1028                 free(skc->sc_kctx.skc_session_key.value);
1029         }
1030
1031         if (skc->sc_params)
1032                 DH_free(skc->sc_params);
1033
1034         free(skc);
1035         skc = NULL;
1036 }
1037
1038 /* This function handles key derivation using the hash algorithm specified in
1039  * \a hash_alg, buffers in \a key_binding_bufs, and original key in
1040  * \a origin_key to produce a \a derived_key.  The first element of the
1041  * key_binding_bufs array is reserved for the counter used in the KDF.  The
1042  * derived key in \a derived_key could differ in size from \a origin_key and
1043  * must be populated with the expected size and a valid buffer to hold the
1044  * contents.
1045  *
1046  * If the derived key size is greater than the HMAC algorithm size it will be
1047  * a done using several iterations of a counter and the key binding bufs.
1048  *
1049  * If the size is smaller it will take copy the first N bytes necessary to
1050  * fill the derived key. */
1051 int sk_kdf(gss_buffer_desc *derived_key , gss_buffer_desc *origin_key,
1052            gss_buffer_desc *key_binding_bufs, int numbufs, int hmac_alg)
1053 {
1054         size_t remain;
1055         size_t bytes;
1056         uint32_t counter;
1057         char *keydata;
1058         gss_buffer_desc tmp_hash;
1059         int i;
1060         int rc;
1061
1062         if (numbufs < 1)
1063                 return -EINVAL;
1064
1065         /* Use a counter as the first buffer followed by the key binding
1066          * buffers in the event we need more than one a single cycle to
1067          * produced a symmetric key large enough in size */
1068         key_binding_bufs[0].value = &counter;
1069         key_binding_bufs[0].length = sizeof(counter);
1070
1071         remain = derived_key->length;
1072         keydata = derived_key->value;
1073         i = 0;
1074         while (remain > 0) {
1075                 counter = htobe32(i++);
1076                 rc = sk_sign_bufs(origin_key, key_binding_bufs, numbufs,
1077                                   sk_hash_to_evp_md(hmac_alg), &tmp_hash);
1078                 if (rc) {
1079                         if (tmp_hash.value)
1080                                 free(tmp_hash.value);
1081                         return rc;
1082                 }
1083
1084                 LASSERT(sk_hmac_types[hmac_alg].sht_bytes ==
1085                         tmp_hash.length);
1086
1087                 bytes = (remain < tmp_hash.length) ? remain : tmp_hash.length;
1088                 memcpy(keydata, tmp_hash.value, bytes);
1089                 free(tmp_hash.value);
1090                 remain -= bytes;
1091                 keydata += bytes;
1092         }
1093
1094         return 0;
1095 }
1096
1097 /* Populates the sk_cred's session_key using the a Key Derviation Function (KDF)
1098  * based on the recommendations in NIST Special Publication SP 800-56B Rev 1
1099  * (Sep 2014) Section 5.5.1
1100  *
1101  * \param[in,out]       skc             Shared key credentials structure with
1102  *
1103  * \return      -1              failure
1104  * \return      0               success
1105  */
1106 int sk_session_kdf(struct sk_cred *skc, lnet_nid_t client_nid,
1107                    gss_buffer_desc *client_token, gss_buffer_desc *server_token)
1108 {
1109         struct sk_kernel_ctx *kctx = &skc->sc_kctx;
1110         gss_buffer_desc *session_key = &kctx->skc_session_key;
1111         gss_buffer_desc bufs[5];
1112         int rc = -1;
1113
1114         session_key->length = sk_crypt_types[kctx->skc_crypt_alg].sct_bytes;
1115         session_key->value = malloc(session_key->length);
1116         if (!session_key->value) {
1117                 printerr(0, "Failed to allocate memory for session key\n");
1118                 return rc;
1119         }
1120
1121         /* Key binding info ordering
1122          * 1. Reserved for counter
1123          * 1. DH shared key
1124          * 2. Client's NIDs
1125          * 3. Client's token
1126          * 4. Server's token */
1127         bufs[0].value = NULL;
1128         bufs[0].length = 0;
1129         bufs[1] = skc->sc_dh_shared_key;
1130         bufs[2].value = &client_nid;
1131         bufs[2].length = sizeof(client_nid);
1132         bufs[3] = *client_token;
1133         bufs[4] = *server_token;
1134
1135         return sk_kdf(&kctx->skc_session_key, &kctx->skc_shared_key, bufs,
1136                       5, kctx->skc_hmac_alg);
1137 }
1138
1139 /* Uses the session key to create an HMAC key and encryption key.  In
1140  * integrity mode the session key used to generate the HMAC key uses
1141  * session information which is available on the wire but by creating
1142  * a session based HMAC key we can prevent potential replay as both the
1143  * client and server have random numbers used as part of the key creation.
1144  *
1145  * The keys used for integrity and privacy are formulated as below using
1146  * the session key that is the output of the key derivation function.  The
1147  * HMAC algorithm is determined by the shared key algorithm selected in the
1148  * key file.
1149  *
1150  * For ski mode:
1151  * Session HMAC Key = PBKDF2("Integrity", KDF derived Session Key)
1152  *
1153  * For skpi mode:
1154  * Session HMAC Key = PBKDF2("Integrity", KDF derived Session Key)
1155  * Session Encryption Key = PBKDF2("Encrypt", KDF derived Session Key)
1156  *
1157  * \param[in,out]       skc             Shared key credentials structure with
1158  *
1159  * \return      -1              failure
1160  * \return      0               success
1161  */
1162 int sk_compute_keys(struct sk_cred *skc)
1163 {
1164         struct sk_kernel_ctx *kctx = &skc->sc_kctx;
1165         gss_buffer_desc *session_key = &kctx->skc_session_key;
1166         gss_buffer_desc *hmac_key = &kctx->skc_hmac_key;
1167         gss_buffer_desc *encrypt_key = &kctx->skc_encrypt_key;
1168         char *encrypt = "Encrypt";
1169         char *integrity = "Integrity";
1170         int rc;
1171
1172         hmac_key->length = sk_hmac_types[kctx->skc_hmac_alg].sht_bytes;
1173         hmac_key->value = malloc(hmac_key->length);
1174         if (!hmac_key->value)
1175                 return -ENOMEM;
1176
1177         rc = PKCS5_PBKDF2_HMAC(integrity, -1, session_key->value,
1178                                session_key->length, SK_PBKDF2_ITERATIONS,
1179                                sk_hash_to_evp_md(kctx->skc_hmac_alg),
1180                                hmac_key->length, hmac_key->value);
1181         if (rc == 0)
1182                 return -EINVAL;
1183
1184         /* Encryption key is only populated in privacy mode */
1185         if ((skc->sc_flags & LGSS_SVC_PRIV) == 0)
1186                 return 0;
1187
1188         encrypt_key->length = sk_crypt_types[kctx->skc_crypt_alg].sct_bytes;
1189         encrypt_key->value = malloc(encrypt_key->length);
1190         if (!encrypt_key->value)
1191                 return -ENOMEM;
1192
1193         rc = PKCS5_PBKDF2_HMAC(encrypt, -1, session_key->value,
1194                                session_key->length, SK_PBKDF2_ITERATIONS,
1195                                sk_hash_to_evp_md(kctx->skc_hmac_alg),
1196                                encrypt_key->length, encrypt_key->value);
1197         if (rc == 0)
1198                 return -EINVAL;
1199
1200         return 0;
1201 }
1202
1203 /**
1204  * Computes a session key based on the DH parameters from the host and its peer
1205  *
1206  * \param[in,out]       skc             Shared key credentials structure with
1207  *                                      the session key populated with the
1208  *                                      compute key
1209  * \param[in]           pub_key         Public key returned from peer in
1210  *                                      gss_buffer_desc
1211  * \return      gss error               failure
1212  * \return      GSS_S_COMPLETE          success
1213  */
1214 uint32_t sk_compute_dh_key(struct sk_cred *skc, const gss_buffer_desc *pub_key)
1215 {
1216         gss_buffer_desc *dh_shared = &skc->sc_dh_shared_key;
1217         BIGNUM *remote_pub_key;
1218         int status;
1219         uint32_t rc = GSS_S_FAILURE;
1220
1221         /* No keys computed unless privacy mode is in use */
1222         if ((skc->sc_flags & LGSS_SVC_PRIV) == 0)
1223                 return GSS_S_COMPLETE;
1224
1225         remote_pub_key = BN_bin2bn(pub_key->value, pub_key->length, NULL);
1226         if (!remote_pub_key) {
1227                 printerr(0, "Failed to convert binary to BIGNUM\n");
1228                 return rc;
1229         }
1230
1231         dh_shared->length = DH_size(skc->sc_params);
1232         dh_shared->value = malloc(dh_shared->length);
1233         if (!dh_shared->value) {
1234                 printerr(0, "Failed to allocate memory for computed shared "
1235                          "secret key\n");
1236                 goto out_err;
1237         }
1238
1239         /* This compute the shared key from the DHKE */
1240         status = DH_compute_key(dh_shared->value, remote_pub_key,
1241                                 skc->sc_params);
1242         if (status == -1) {
1243                 printerr(0, "DH_compute_key() failed: %s\n",
1244                          ERR_error_string(ERR_get_error(), NULL));
1245                 goto out_err;
1246         } else if (status < dh_shared->length) {
1247                 printerr(0, "DH_compute_key() returned a short key of %d "
1248                          "bytes, expected: %zu\n", status, dh_shared->length);
1249                 rc = GSS_S_DEFECTIVE_TOKEN;
1250                 goto out_err;
1251         }
1252
1253         rc = GSS_S_COMPLETE;
1254
1255 out_err:
1256         BN_free(remote_pub_key);
1257         return rc;
1258 }
1259
1260 /**
1261  * Creates a serialized buffer for the kernel in the order of struct
1262  * sk_kernel_ctx.
1263  *
1264  * \param[in,out]       skc             Shared key credentials structure
1265  * \param[in,out]       ctx_token       Serialized buffer for kernel.
1266  *                                      Caller must free this buffer.
1267  *
1268  * \return      0       success
1269  * \return      -1      failure
1270  */
1271 int sk_serialize_kctx(struct sk_cred *skc, gss_buffer_desc *ctx_token)
1272 {
1273         struct sk_kernel_ctx *kctx = &skc->sc_kctx;
1274         char *p, *end;
1275         size_t bufsize;
1276
1277         bufsize = sizeof(*kctx) + kctx->skc_hmac_key.length +
1278                   kctx->skc_encrypt_key.length;
1279
1280         ctx_token->value = malloc(bufsize);
1281         if (!ctx_token->value)
1282                 return -1;
1283         ctx_token->length = bufsize;
1284
1285         p = ctx_token->value;
1286         end = p + ctx_token->length;
1287
1288         if (WRITE_BYTES(&p, end, kctx->skc_version))
1289                 return -1;
1290         if (WRITE_BYTES(&p, end, kctx->skc_hmac_alg))
1291                 return -1;
1292         if (WRITE_BYTES(&p, end, kctx->skc_crypt_alg))
1293                 return -1;
1294         if (WRITE_BYTES(&p, end, kctx->skc_expire))
1295                 return -1;
1296         if (WRITE_BYTES(&p, end, kctx->skc_host_random))
1297                 return -1;
1298         if (WRITE_BYTES(&p, end, kctx->skc_peer_random))
1299                 return -1;
1300         if (write_buffer(&p, end, &kctx->skc_hmac_key))
1301                 return -1;
1302         if (write_buffer(&p, end, &kctx->skc_encrypt_key))
1303                 return -1;
1304
1305         printerr(2, "Serialized buffer of %zu bytes for kernel\n", bufsize);
1306
1307         return 0;
1308 }
1309
1310 /**
1311  * Decodes a netstring \a ns into array of gss_buffer_descs at \a bufs
1312  * up to \a numbufs.  Memory is allocated for each value and length
1313  * will be populated with the length
1314  *
1315  * \param[in,out]       bufs    Array of gss_buffer_descs
1316  * \param[in,out]       numbufs number of gss_buffer_desc in array
1317  * \param[in]           ns      netstring to decode
1318  *
1319  * \return      buffers populated       success
1320  * \return      -1                      failure
1321  */
1322 int sk_decode_netstring(gss_buffer_desc *bufs, int numbufs, gss_buffer_desc *ns)
1323 {
1324         char *ptr = ns->value;
1325         size_t remain = ns->length;
1326         unsigned int size;
1327         int digits;
1328         int sep;
1329         int rc;
1330         int i;
1331
1332         for (i = 0; i < numbufs; i++) {
1333                 /* read the size of first buffer */
1334                 rc = sscanf(ptr, "%9u", &size);
1335                 if (rc < 1)
1336                         goto out_err;
1337                 digits = (size) ? ceil(log10(size + 1)) : 1;
1338
1339                 /* sep of current string */
1340                 sep = size + digits + 2;
1341
1342                 /* check to make sure it's valid */
1343                 if (remain < sep || ptr[digits] != ':' ||
1344                     ptr[sep - 1] != ',')
1345                         goto out_err;
1346
1347                 bufs[i].length = size;
1348                 if (size == 0) {
1349                         bufs[i].value = NULL;
1350                 } else {
1351                         bufs[i].value = malloc(size);
1352                         if (!bufs[i].value)
1353                                 goto out_err;
1354                         memcpy(bufs[i].value, &ptr[digits + 1], size);
1355                 }
1356
1357                 remain -= sep;
1358                 ptr += sep;
1359         }
1360
1361         printerr(2, "Decoded netstring of %zu bytes\n", ns->length);
1362         return i;
1363
1364 out_err:
1365         while (i-- > 0) {
1366                 if (bufs[i].value)
1367                         free(bufs[i].value);
1368                 bufs[i].length = 0;
1369         }
1370         return -1;
1371 }
1372
1373 /**
1374  * Creates a netstring in a gss_buffer_desc that consists of all
1375  * the gss_buffer_desc found in \a bufs.  The netstring should be treated
1376  * as binary as it can contain null characters.
1377  *
1378  * \param[in]           bufs            Array of gss_buffer_desc to use as input
1379  * \param[in]           numbufs         Number of buffers in array
1380  * \param[in,out]       ns              Destination gss_buffer_desc to hold
1381  *                                      netstring
1382  *
1383  * \return      -1      failure
1384  * \return      0       success
1385  */
1386 int sk_encode_netstring(gss_buffer_desc *bufs, int numbufs,
1387                         gss_buffer_desc *ns)
1388 {
1389         unsigned char *ptr;
1390         int size = 0;
1391         int rc;
1392         int i;
1393
1394         /* size of string in decimal, string size, colon, and comma */
1395         for (i = 0; i < numbufs; i++) {
1396
1397                 if (bufs[i].length == 0)
1398                         size += 3;
1399                 else
1400                         size += ceil(log10(bufs[i].length + 1)) +
1401                                 bufs[i].length + 2;
1402         }
1403
1404         ns->length = size;
1405         ns->value = malloc(ns->length);
1406         if (!ns->value) {
1407                 ns->length = 0;
1408                 return -1;
1409         }
1410
1411         ptr = ns->value;
1412         for (i = 0; i < numbufs; i++) {
1413                 /* size */
1414                 rc = snprintf((char *) ptr, size, "%zu:", bufs[i].length);
1415                 ptr += rc;
1416
1417                 /* contents */
1418                 memcpy(ptr, bufs[i].value, bufs[i].length);
1419                 ptr += bufs[i].length;
1420
1421                 /* delimeter */
1422                 *ptr++ = ',';
1423
1424                 size -= bufs[i].length + rc + 1;
1425
1426                 /* should not happen */
1427                 if (size < 0)
1428                         abort();
1429         }
1430
1431         printerr(2, "Encoded netstring of %zu bytes\n", ns->length);
1432         return 0;
1433 }