1*5fff9558SSimon J. Gerraty /*- 2*5fff9558SSimon J. Gerraty * Copyright (c) 2018, Juniper Networks, Inc. 3*5fff9558SSimon J. Gerraty * 4*5fff9558SSimon J. Gerraty * Redistribution and use in source and binary forms, with or without 5*5fff9558SSimon J. Gerraty * modification, are permitted provided that the following conditions 6*5fff9558SSimon J. Gerraty * are met: 7*5fff9558SSimon J. Gerraty * 1. Redistributions of source code must retain the above copyright 8*5fff9558SSimon J. Gerraty * notice, this list of conditions and the following disclaimer. 9*5fff9558SSimon J. Gerraty * 2. Redistributions in binary form must reproduce the above copyright 10*5fff9558SSimon J. Gerraty * notice, this list of conditions and the following disclaimer in the 11*5fff9558SSimon J. Gerraty * documentation and/or other materials provided with the distribution. 12*5fff9558SSimon J. Gerraty * 13*5fff9558SSimon J. Gerraty * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 14*5fff9558SSimon J. Gerraty * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 15*5fff9558SSimon J. Gerraty * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 16*5fff9558SSimon J. Gerraty * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 17*5fff9558SSimon J. Gerraty * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 18*5fff9558SSimon J. Gerraty * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 19*5fff9558SSimon J. Gerraty * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20*5fff9558SSimon J. Gerraty * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21*5fff9558SSimon J. Gerraty * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22*5fff9558SSimon J. Gerraty * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23*5fff9558SSimon J. Gerraty * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24*5fff9558SSimon J. Gerraty */ 25*5fff9558SSimon J. Gerraty 26*5fff9558SSimon J. Gerraty #include <sys/cdefs.h> 27*5fff9558SSimon J. Gerraty __FBSDID("$FreeBSD$"); 28*5fff9558SSimon J. Gerraty 29*5fff9558SSimon J. Gerraty #include "../libsecureboot-priv.h" 30*5fff9558SSimon J. Gerraty 31*5fff9558SSimon J. Gerraty #include "decode.h" 32*5fff9558SSimon J. Gerraty #include "packet.h" 33*5fff9558SSimon J. Gerraty 34*5fff9558SSimon J. Gerraty /** 35*5fff9558SSimon J. Gerraty * @brief decode user-id packet 36*5fff9558SSimon J. Gerraty * 37*5fff9558SSimon J. Gerraty * This is trivial 38*5fff9558SSimon J. Gerraty * 39*5fff9558SSimon J. Gerraty * @sa rfc4880:5.11 40*5fff9558SSimon J. Gerraty */ 41*5fff9558SSimon J. Gerraty ssize_t 42*5fff9558SSimon J. Gerraty decode_user(int tag, unsigned char **pptr, size_t len, OpenPGP_user *user) 43*5fff9558SSimon J. Gerraty { 44*5fff9558SSimon J. Gerraty char *cp; 45*5fff9558SSimon J. Gerraty 46*5fff9558SSimon J. Gerraty if (tag == 13) { 47*5fff9558SSimon J. Gerraty user->id = malloc(len + 1); 48*5fff9558SSimon J. Gerraty strncpy(user->id, (char *)*pptr, len); 49*5fff9558SSimon J. Gerraty user->id[len] = '\0'; 50*5fff9558SSimon J. Gerraty user->name = user->id; 51*5fff9558SSimon J. Gerraty cp = strchr(user->id, '<'); 52*5fff9558SSimon J. Gerraty if (cp > user->id) { 53*5fff9558SSimon J. Gerraty user->id = strdup(user->id); 54*5fff9558SSimon J. Gerraty cp[-1] = '\0'; 55*5fff9558SSimon J. Gerraty } 56*5fff9558SSimon J. Gerraty } 57*5fff9558SSimon J. Gerraty *pptr += len; 58*5fff9558SSimon J. Gerraty return ((ssize_t)len); 59*5fff9558SSimon J. Gerraty } 60*5fff9558SSimon J. Gerraty 61*5fff9558SSimon J. Gerraty /** 62*5fff9558SSimon J. Gerraty * @brief decode a key packet 63*5fff9558SSimon J. Gerraty * 64*5fff9558SSimon J. Gerraty * We only really support v4 and RSA 65*5fff9558SSimon J. Gerraty * 66*5fff9558SSimon J. Gerraty * @sa rfc4880:5.5.1.1 67*5fff9558SSimon J. Gerraty */ 68*5fff9558SSimon J. Gerraty ssize_t 69*5fff9558SSimon J. Gerraty decode_key(int tag, unsigned char **pptr, size_t len, OpenPGP_key *key) 70*5fff9558SSimon J. Gerraty { 71*5fff9558SSimon J. Gerraty unsigned char *ptr; 72*5fff9558SSimon J. Gerraty int version; 73*5fff9558SSimon J. Gerraty #ifdef USE_BEARSSL 74*5fff9558SSimon J. Gerraty br_sha1_context mctx; 75*5fff9558SSimon J. Gerraty unsigned char mdata[br_sha512_SIZE]; 76*5fff9558SSimon J. Gerraty size_t mlen; 77*5fff9558SSimon J. Gerraty #else 78*5fff9558SSimon J. Gerraty RSA *rsa = NULL; 79*5fff9558SSimon J. Gerraty const EVP_MD *md = NULL; 80*5fff9558SSimon J. Gerraty EVP_MD_CTX mctx; 81*5fff9558SSimon J. Gerraty unsigned char mdata[EVP_MAX_MD_SIZE]; 82*5fff9558SSimon J. Gerraty unsigned int mlen; 83*5fff9558SSimon J. Gerraty #endif 84*5fff9558SSimon J. Gerraty 85*5fff9558SSimon J. Gerraty if (tag != 6) 86*5fff9558SSimon J. Gerraty return (-1); 87*5fff9558SSimon J. Gerraty 88*5fff9558SSimon J. Gerraty key->key = NULL; 89*5fff9558SSimon J. Gerraty ptr = *pptr; 90*5fff9558SSimon J. Gerraty version = *ptr; 91*5fff9558SSimon J. Gerraty if (version == 4) { /* all we support really */ 92*5fff9558SSimon J. Gerraty /* comput key fingerprint and id @sa rfc4880:12.2 */ 93*5fff9558SSimon J. Gerraty mdata[0] = 0x99; /* rfc4880: 12.2.a.1 */ 94*5fff9558SSimon J. Gerraty mdata[1] = (len >> 8) & 0xff; 95*5fff9558SSimon J. Gerraty mdata[2] = len & 0xff; 96*5fff9558SSimon J. Gerraty 97*5fff9558SSimon J. Gerraty #ifdef USE_BEARSSL 98*5fff9558SSimon J. Gerraty br_sha1_init(&mctx); 99*5fff9558SSimon J. Gerraty br_sha1_update(&mctx, mdata, 3); 100*5fff9558SSimon J. Gerraty br_sha1_update(&mctx, ptr, len); 101*5fff9558SSimon J. Gerraty br_sha1_out(&mctx, mdata); 102*5fff9558SSimon J. Gerraty mlen = br_sha1_SIZE; 103*5fff9558SSimon J. Gerraty #else 104*5fff9558SSimon J. Gerraty md = EVP_get_digestbyname("sha1"); 105*5fff9558SSimon J. Gerraty EVP_DigestInit(&mctx, md); 106*5fff9558SSimon J. Gerraty EVP_DigestUpdate(&mctx, mdata, 3); 107*5fff9558SSimon J. Gerraty EVP_DigestUpdate(&mctx, ptr, len); 108*5fff9558SSimon J. Gerraty mlen = (unsigned int)sizeof(mdata); 109*5fff9558SSimon J. Gerraty EVP_DigestFinal(&mctx, mdata, &mlen); 110*5fff9558SSimon J. Gerraty #endif 111*5fff9558SSimon J. Gerraty key->id = octets2hex(&mdata[mlen - 8], 8); 112*5fff9558SSimon J. Gerraty } 113*5fff9558SSimon J. Gerraty ptr += 1; /* done with version */ 114*5fff9558SSimon J. Gerraty ptr += 4; /* skip ctime */ 115*5fff9558SSimon J. Gerraty if (version == 3) 116*5fff9558SSimon J. Gerraty ptr += 2; /* valid days */ 117*5fff9558SSimon J. Gerraty key->sig_alg = *ptr++; 118*5fff9558SSimon J. Gerraty if (key->sig_alg == 1) { /* RSA */ 119*5fff9558SSimon J. Gerraty #ifdef USE_BEARSSL 120*5fff9558SSimon J. Gerraty key->key = NEW(br_rsa_public_key); 121*5fff9558SSimon J. Gerraty if (!key->key) 122*5fff9558SSimon J. Gerraty goto oops; 123*5fff9558SSimon J. Gerraty key->key->n = mpi2bn(&ptr, &key->key->nlen); 124*5fff9558SSimon J. Gerraty key->key->e = mpi2bn(&ptr, &key->key->elen); 125*5fff9558SSimon J. Gerraty #else 126*5fff9558SSimon J. Gerraty rsa = RSA_new(); 127*5fff9558SSimon J. Gerraty if (!rsa) 128*5fff9558SSimon J. Gerraty goto oops; 129*5fff9558SSimon J. Gerraty rsa->n = mpi2bn(&ptr); 130*5fff9558SSimon J. Gerraty rsa->e = mpi2bn(&ptr); 131*5fff9558SSimon J. Gerraty key->key = EVP_PKEY_new(); 132*5fff9558SSimon J. Gerraty if (!key->key || !rsa->n || !rsa->e) { 133*5fff9558SSimon J. Gerraty goto oops; 134*5fff9558SSimon J. Gerraty } 135*5fff9558SSimon J. Gerraty if (!EVP_PKEY_set1_RSA(key->key, rsa)) 136*5fff9558SSimon J. Gerraty goto oops; 137*5fff9558SSimon J. Gerraty #endif 138*5fff9558SSimon J. Gerraty } 139*5fff9558SSimon J. Gerraty /* we are done */ 140*5fff9558SSimon J. Gerraty return ((ssize_t)len); 141*5fff9558SSimon J. Gerraty oops: 142*5fff9558SSimon J. Gerraty #ifdef USE_BEARSSL 143*5fff9558SSimon J. Gerraty free(key->key); 144*5fff9558SSimon J. Gerraty key->key = NULL; 145*5fff9558SSimon J. Gerraty #else 146*5fff9558SSimon J. Gerraty if (rsa) 147*5fff9558SSimon J. Gerraty RSA_free(rsa); 148*5fff9558SSimon J. Gerraty if (key->key) { 149*5fff9558SSimon J. Gerraty EVP_PKEY_free(key->key); 150*5fff9558SSimon J. Gerraty key->key = NULL; 151*5fff9558SSimon J. Gerraty } 152*5fff9558SSimon J. Gerraty #endif 153*5fff9558SSimon J. Gerraty return (-1); 154*5fff9558SSimon J. Gerraty } 155*5fff9558SSimon J. Gerraty 156*5fff9558SSimon J. Gerraty static OpenPGP_key * 157*5fff9558SSimon J. Gerraty load_key_buf(unsigned char *buf, size_t nbytes) 158*5fff9558SSimon J. Gerraty { 159*5fff9558SSimon J. Gerraty unsigned char *data = NULL; 160*5fff9558SSimon J. Gerraty unsigned char *ptr; 161*5fff9558SSimon J. Gerraty ssize_t rc; 162*5fff9558SSimon J. Gerraty int tag; 163*5fff9558SSimon J. Gerraty OpenPGP_key *key; 164*5fff9558SSimon J. Gerraty 165*5fff9558SSimon J. Gerraty if (!buf) 166*5fff9558SSimon J. Gerraty return (NULL); 167*5fff9558SSimon J. Gerraty 168*5fff9558SSimon J. Gerraty initialize(); 169*5fff9558SSimon J. Gerraty 170*5fff9558SSimon J. Gerraty if (!(buf[0] & OPENPGP_TAG_ISTAG)) { 171*5fff9558SSimon J. Gerraty data = dearmor((char *)buf, nbytes, &nbytes); 172*5fff9558SSimon J. Gerraty ptr = data; 173*5fff9558SSimon J. Gerraty } else 174*5fff9558SSimon J. Gerraty ptr = buf; 175*5fff9558SSimon J. Gerraty key = NEW(OpenPGP_key); 176*5fff9558SSimon J. Gerraty if (key) { 177*5fff9558SSimon J. Gerraty rc = decode_packet(0, &ptr, nbytes, (decoder_t)decode_key, 178*5fff9558SSimon J. Gerraty key); 179*5fff9558SSimon J. Gerraty if (rc < 0) { 180*5fff9558SSimon J. Gerraty free(key); 181*5fff9558SSimon J. Gerraty key = NULL; 182*5fff9558SSimon J. Gerraty } else if (rc > 8) { 183*5fff9558SSimon J. Gerraty int isnew, ltype; 184*5fff9558SSimon J. Gerraty 185*5fff9558SSimon J. Gerraty tag = decode_tag(ptr, &isnew, <ype); 186*5fff9558SSimon J. Gerraty if (tag == 13) { 187*5fff9558SSimon J. Gerraty key->user = NEW(OpenPGP_user); 188*5fff9558SSimon J. Gerraty rc = decode_packet(0, &ptr, (size_t)rc, 189*5fff9558SSimon J. Gerraty (decoder_t)decode_user, key->user); 190*5fff9558SSimon J. Gerraty } 191*5fff9558SSimon J. Gerraty } 192*5fff9558SSimon J. Gerraty } 193*5fff9558SSimon J. Gerraty free(data); 194*5fff9558SSimon J. Gerraty return (key); 195*5fff9558SSimon J. Gerraty } 196*5fff9558SSimon J. Gerraty 197*5fff9558SSimon J. Gerraty static LIST_HEAD(, OpenPGP_key_) trust_list; 198*5fff9558SSimon J. Gerraty 199*5fff9558SSimon J. Gerraty /** 200*5fff9558SSimon J. Gerraty * @brief add a key to our list 201*5fff9558SSimon J. Gerraty */ 202*5fff9558SSimon J. Gerraty void 203*5fff9558SSimon J. Gerraty openpgp_trust_add(OpenPGP_key *key) 204*5fff9558SSimon J. Gerraty { 205*5fff9558SSimon J. Gerraty static int once = 0; 206*5fff9558SSimon J. Gerraty 207*5fff9558SSimon J. Gerraty if (!once) { 208*5fff9558SSimon J. Gerraty once = 1; 209*5fff9558SSimon J. Gerraty 210*5fff9558SSimon J. Gerraty LIST_INIT(&trust_list); 211*5fff9558SSimon J. Gerraty } 212*5fff9558SSimon J. Gerraty if (key) 213*5fff9558SSimon J. Gerraty LIST_INSERT_HEAD(&trust_list, key, entries); 214*5fff9558SSimon J. Gerraty } 215*5fff9558SSimon J. Gerraty 216*5fff9558SSimon J. Gerraty /** 217*5fff9558SSimon J. Gerraty * @brief if keyID is in our list return the key 218*5fff9558SSimon J. Gerraty * 219*5fff9558SSimon J. Gerraty * @return key or NULL 220*5fff9558SSimon J. Gerraty */ 221*5fff9558SSimon J. Gerraty OpenPGP_key * 222*5fff9558SSimon J. Gerraty openpgp_trust_get(const char *keyID) 223*5fff9558SSimon J. Gerraty { 224*5fff9558SSimon J. Gerraty OpenPGP_key *key; 225*5fff9558SSimon J. Gerraty 226*5fff9558SSimon J. Gerraty openpgp_trust_add(NULL); /* initialize if needed */ 227*5fff9558SSimon J. Gerraty 228*5fff9558SSimon J. Gerraty LIST_FOREACH(key, &trust_list, entries) { 229*5fff9558SSimon J. Gerraty if (strcmp(key->id, keyID) == 0) 230*5fff9558SSimon J. Gerraty return (key); 231*5fff9558SSimon J. Gerraty } 232*5fff9558SSimon J. Gerraty return (NULL); 233*5fff9558SSimon J. Gerraty } 234*5fff9558SSimon J. Gerraty 235*5fff9558SSimon J. Gerraty /** 236*5fff9558SSimon J. Gerraty * @brief load a key from file 237*5fff9558SSimon J. Gerraty */ 238*5fff9558SSimon J. Gerraty OpenPGP_key * 239*5fff9558SSimon J. Gerraty load_key_file(const char *kfile) 240*5fff9558SSimon J. Gerraty { 241*5fff9558SSimon J. Gerraty unsigned char *data = NULL; 242*5fff9558SSimon J. Gerraty size_t n; 243*5fff9558SSimon J. Gerraty OpenPGP_key *key; 244*5fff9558SSimon J. Gerraty 245*5fff9558SSimon J. Gerraty data = read_file(kfile, &n); 246*5fff9558SSimon J. Gerraty key = load_key_buf(data, n); 247*5fff9558SSimon J. Gerraty free(data); 248*5fff9558SSimon J. Gerraty openpgp_trust_add(key); 249*5fff9558SSimon J. Gerraty return (key); 250*5fff9558SSimon J. Gerraty } 251*5fff9558SSimon J. Gerraty 252*5fff9558SSimon J. Gerraty #include <ta_asc.h> 253*5fff9558SSimon J. Gerraty 254*5fff9558SSimon J. Gerraty #ifndef _STANDALONE 255*5fff9558SSimon J. Gerraty /* we can lookup keyID in filesystem */ 256*5fff9558SSimon J. Gerraty 257*5fff9558SSimon J. Gerraty static const char *trust_store[] = { 258*5fff9558SSimon J. Gerraty "/var/db/trust", 259*5fff9558SSimon J. Gerraty "/etc/db/trust", 260*5fff9558SSimon J. Gerraty NULL, 261*5fff9558SSimon J. Gerraty }; 262*5fff9558SSimon J. Gerraty 263*5fff9558SSimon J. Gerraty /** 264*5fff9558SSimon J. Gerraty * @brief lookup key id in trust store 265*5fff9558SSimon J. Gerraty * 266*5fff9558SSimon J. Gerraty */ 267*5fff9558SSimon J. Gerraty static OpenPGP_key * 268*5fff9558SSimon J. Gerraty load_trusted_key_id(const char *keyID) 269*5fff9558SSimon J. Gerraty { 270*5fff9558SSimon J. Gerraty char kfile[MAXPATHLEN]; 271*5fff9558SSimon J. Gerraty const char **tp; 272*5fff9558SSimon J. Gerraty size_t n; 273*5fff9558SSimon J. Gerraty 274*5fff9558SSimon J. Gerraty for (tp = trust_store; *tp; tp++) { 275*5fff9558SSimon J. Gerraty n = (size_t)snprintf(kfile, sizeof(kfile), "%s/%s", *tp, keyID); 276*5fff9558SSimon J. Gerraty if (n >= sizeof(kfile)) 277*5fff9558SSimon J. Gerraty return (NULL); 278*5fff9558SSimon J. Gerraty if (access(kfile, R_OK) == 0) { 279*5fff9558SSimon J. Gerraty return (load_key_file(kfile)); 280*5fff9558SSimon J. Gerraty } 281*5fff9558SSimon J. Gerraty } 282*5fff9558SSimon J. Gerraty return (NULL); 283*5fff9558SSimon J. Gerraty } 284*5fff9558SSimon J. Gerraty #endif 285*5fff9558SSimon J. Gerraty 286*5fff9558SSimon J. Gerraty /** 287*5fff9558SSimon J. Gerraty * @brief return key if trusted 288*5fff9558SSimon J. Gerraty */ 289*5fff9558SSimon J. Gerraty OpenPGP_key * 290*5fff9558SSimon J. Gerraty load_key_id(const char *keyID) 291*5fff9558SSimon J. Gerraty { 292*5fff9558SSimon J. Gerraty static int once = 0; 293*5fff9558SSimon J. Gerraty OpenPGP_key *key; 294*5fff9558SSimon J. Gerraty 295*5fff9558SSimon J. Gerraty if (!once) { 296*5fff9558SSimon J. Gerraty #ifdef HAVE_TA_ASC 297*5fff9558SSimon J. Gerraty const char **tp; 298*5fff9558SSimon J. Gerraty char *cp; 299*5fff9558SSimon J. Gerraty size_t n; 300*5fff9558SSimon J. Gerraty 301*5fff9558SSimon J. Gerraty for (tp = ta_ASC; *tp; tp++) { 302*5fff9558SSimon J. Gerraty if ((cp = strdup(*tp))) { 303*5fff9558SSimon J. Gerraty n = strlen(cp); 304*5fff9558SSimon J. Gerraty key = load_key_buf((unsigned char *)cp, n); 305*5fff9558SSimon J. Gerraty free(cp); 306*5fff9558SSimon J. Gerraty openpgp_trust_add(key); 307*5fff9558SSimon J. Gerraty } 308*5fff9558SSimon J. Gerraty } 309*5fff9558SSimon J. Gerraty #endif 310*5fff9558SSimon J. Gerraty once = 1; 311*5fff9558SSimon J. Gerraty } 312*5fff9558SSimon J. Gerraty key = openpgp_trust_get(keyID); 313*5fff9558SSimon J. Gerraty #ifndef _STANDALONE 314*5fff9558SSimon J. Gerraty if (!key) 315*5fff9558SSimon J. Gerraty key = load_trusted_key_id(keyID); 316*5fff9558SSimon J. Gerraty #endif 317*5fff9558SSimon J. Gerraty return (key); 318*5fff9558SSimon J. Gerraty } 319*5fff9558SSimon J. Gerraty 320*5fff9558SSimon J. Gerraty /** 321*5fff9558SSimon J. Gerraty * @brief test that we can verify a signature 322*5fff9558SSimon J. Gerraty * 323*5fff9558SSimon J. Gerraty * Unlike X.509 certificates, we only support RSA keys 324*5fff9558SSimon J. Gerraty * so we stop after first successful signature verification 325*5fff9558SSimon J. Gerraty * (which should also be the first attempt ;-) 326*5fff9558SSimon J. Gerraty */ 327*5fff9558SSimon J. Gerraty int 328*5fff9558SSimon J. Gerraty openpgp_self_tests(void) 329*5fff9558SSimon J. Gerraty { 330*5fff9558SSimon J. Gerraty static int rc = -1; /* remember result */ 331*5fff9558SSimon J. Gerraty #ifdef HAVE_VC_ASC 332*5fff9558SSimon J. Gerraty const char **vp, **tp; 333*5fff9558SSimon J. Gerraty char *fdata, *sdata = NULL; 334*5fff9558SSimon J. Gerraty size_t fbytes, sbytes; 335*5fff9558SSimon J. Gerraty 336*5fff9558SSimon J. Gerraty for (tp = ta_ASC, vp = vc_ASC; *tp && *vp && rc; tp++, vp++) { 337*5fff9558SSimon J. Gerraty if ((fdata = strdup(*tp)) && 338*5fff9558SSimon J. Gerraty (sdata = strdup(*vp))) { 339*5fff9558SSimon J. Gerraty fbytes = strlen(fdata); 340*5fff9558SSimon J. Gerraty sbytes = strlen(sdata); 341*5fff9558SSimon J. Gerraty rc = openpgp_verify("ta_ASC", 342*5fff9558SSimon J. Gerraty (unsigned char *)fdata, fbytes, 343*5fff9558SSimon J. Gerraty (unsigned char *)sdata, sbytes, 0); 344*5fff9558SSimon J. Gerraty printf("Testing verify OpenPGP signature:\t\t%s\n", 345*5fff9558SSimon J. Gerraty rc ? "Failed" : "Passed"); 346*5fff9558SSimon J. Gerraty } 347*5fff9558SSimon J. Gerraty free(fdata); 348*5fff9558SSimon J. Gerraty free(sdata); 349*5fff9558SSimon J. Gerraty } 350*5fff9558SSimon J. Gerraty #endif 351*5fff9558SSimon J. Gerraty return (rc); 352*5fff9558SSimon J. Gerraty } 353