Annotation of embedaddon/axTLS/ssl/p12.c, revision 1.1.1.1

1.1       misho       1: /*
                      2:  * Copyright (c) 2007, Cameron Rich
                      3:  * 
                      4:  * All rights reserved.
                      5:  * 
                      6:  * Redistribution and use in source and binary forms, with or without 
                      7:  * modification, are permitted provided that the following conditions are met:
                      8:  *
                      9:  * * Redistributions of source code must retain the above copyright notice, 
                     10:  *   this list of conditions and the following disclaimer.
                     11:  * * Redistributions in binary form must reproduce the above copyright notice, 
                     12:  *   this list of conditions and the following disclaimer in the documentation 
                     13:  *   and/or other materials provided with the distribution.
                     14:  * * Neither the name of the axTLS project nor the names of its contributors 
                     15:  *   may be used to endorse or promote products derived from this software 
                     16:  *   without specific prior written permission.
                     17:  *
                     18:  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
                     19:  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
                     20:  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
                     21:  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
                     22:  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
                     23:  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
                     24:  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
                     25:  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
                     26:  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
                     27:  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
                     28:  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
                     29:  */
                     30: 
                     31: /**
                     32:  * Process PKCS#8/PKCS#12 keys.
                     33:  *
                     34:  * The decoding of a PKCS#12 key is fairly specific - this code was tested on a
                     35:  * key generated with:
                     36:  *
                     37:  * openssl pkcs12 -export -in axTLS.x509_1024.pem -inkey axTLS.key_1024.pem
                     38:  * -keypbe PBE-SHA1-RC4-128 -certpbe PBE-SHA1-RC4-128 
                     39:  * -name "p12_withoutCA" -out axTLS.withoutCA.p12 -password pass:abcd
                     40:  *
                     41:  * or with a certificate chain:
                     42:  *
                     43:  * openssl pkcs12 -export -in axTLS.x509_1024.pem -inkey axTLS.key_1024.pem
                     44:  * -certfile axTLS.ca_x509.pem -keypbe PBE-SHA1-RC4-128 -certpbe
                     45:  * PBE-SHA1-RC4-128 -name "p12_withCA" -out axTLS.withCA.p12 -password pass:abcd
                     46:  *
                     47:  * Note that the PBE has to be specified with PBE-SHA1-RC4-128. The
                     48:  * private/public keys/certs have to use RSA encryption. Both the integrity
                     49:  * and privacy passwords are the same.
                     50:  *
                     51:  * The PKCS#8 files were generated with something like:
                     52:  *
                     53:  * PEM format:
                     54:  * openssl pkcs8 -in axTLS.key_512.pem -passout pass:abcd -topk8 -v1
                     55:  * PBE-SHA1-RC4-128 -out axTLS.encrypted_pem.p8
                     56:  *
                     57:  * DER format:
                     58:  * openssl pkcs8 -in axTLS.key_512.pem -passout pass:abcd -topk8 -outform DER
                     59:  * -v1 PBE-SHA1-RC4-128 -out axTLS.encrypted.p8
                     60:  */
                     61: 
                     62: #include <stdlib.h>
                     63: #include <string.h>
                     64: #include <stdio.h>
                     65: #include "os_port.h"
                     66: #include "ssl.h"
                     67: 
                     68: /* all commented out if not used */
                     69: #ifdef CONFIG_SSL_USE_PKCS12
                     70: 
                     71: #define BLOCK_SIZE          64
                     72: #define PKCS12_KEY_ID       1
                     73: #define PKCS12_IV_ID        2
                     74: #define PKCS12_MAC_ID       3
                     75: 
                     76: static char *make_uni_pass(const char *password, int *uni_pass_len);
                     77: static int p8_decrypt(const char *uni_pass, int uni_pass_len, 
                     78:                         const uint8_t *salt, int iter, 
                     79:                         uint8_t *priv_key, int priv_key_len, int id);
                     80: static int p8_add_key(SSL_CTX *ssl_ctx, uint8_t *priv_key);
                     81: static int get_pbe_params(uint8_t *buf, int *offset, 
                     82:         const uint8_t **salt, int *iterations);
                     83: 
                     84: /*
                     85:  * Take a raw pkcs8 block and then decrypt it and turn it into a normal key.
                     86:  */
                     87: int pkcs8_decode(SSL_CTX *ssl_ctx, SSLObjLoader *ssl_obj, const char *password)
                     88: {
                     89:     uint8_t *buf = ssl_obj->buf;
                     90:     int len, offset = 0;
                     91:     int iterations;
                     92:     int ret = SSL_NOT_OK;
                     93:     uint8_t *version = NULL;
                     94:     const uint8_t *salt;
                     95:     uint8_t *priv_key;
                     96:     int uni_pass_len;
                     97:     char *uni_pass = make_uni_pass(password, &uni_pass_len);
                     98: 
                     99:     if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0)
                    100:     {
                    101: #ifdef CONFIG_SSL_FULL_MODE
                    102:         printf("Error: Invalid p8 ASN.1 file\n");
                    103: #endif
                    104:         goto error;
                    105:     }
                    106: 
                    107:     /* unencrypted key? */
                    108:     if (asn1_get_int(buf, &offset, &version) > 0 && *version == 0)
                    109:     {
                    110:         ret = p8_add_key(ssl_ctx, buf);
                    111:         goto error;
                    112:     }
                    113: 
                    114:     if (get_pbe_params(buf, &offset, &salt, &iterations) < 0)
                    115:         goto error;
                    116: 
                    117:     if ((len = asn1_next_obj(buf, &offset, ASN1_OCTET_STRING)) < 0)
                    118:         goto error;
                    119: 
                    120:     priv_key = &buf[offset];
                    121: 
                    122:     p8_decrypt(uni_pass, uni_pass_len, salt, 
                    123:                         iterations, priv_key, len, PKCS12_KEY_ID);
                    124:     ret = p8_add_key(ssl_ctx, priv_key);
                    125: 
                    126: error:
                    127:     free(version);
                    128:     free(uni_pass);
                    129:     return ret;
                    130: }
                    131: 
                    132: /*
                    133:  * Take the unencrypted pkcs8 and turn it into a private key 
                    134:  */
                    135: static int p8_add_key(SSL_CTX *ssl_ctx, uint8_t *priv_key)
                    136: {
                    137:     uint8_t *buf = priv_key;
                    138:     int len, offset = 0;
                    139:     int ret = SSL_NOT_OK;
                    140: 
                    141:     /* Skip the preamble and go straight to the private key.
                    142:        We only support rsaEncryption (1.2.840.113549.1.1.1)  */
                    143:     if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
                    144:             asn1_skip_obj(buf, &offset, ASN1_INTEGER) < 0 ||
                    145:             asn1_skip_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
                    146:             (len = asn1_next_obj(buf, &offset, ASN1_OCTET_STRING)) < 0)
                    147:         goto error;
                    148: 
                    149:     ret = asn1_get_private_key(&buf[offset], len, &ssl_ctx->rsa_ctx);
                    150: 
                    151: error:
                    152:     return ret;
                    153: }
                    154: 
                    155: /*
                    156:  * Create the unicode password 
                    157:  */
                    158: static char *make_uni_pass(const char *password, int *uni_pass_len)
                    159: {
                    160:     int pass_len = 0, i;
                    161:     char *uni_pass;
                    162: 
                    163:     if (password == NULL)
                    164:     {
                    165:         password = "";
                    166:     }
                    167: 
                    168:     uni_pass = (char *)malloc((strlen(password)+1)*2);
                    169: 
                    170:     /* modify the password into a unicode version */
                    171:     for (i = 0; i < (int)strlen(password); i++)
                    172:     {
                    173:         uni_pass[pass_len++] = 0;
                    174:         uni_pass[pass_len++] = password[i];
                    175:     }
                    176: 
                    177:     uni_pass[pass_len++] = 0;       /* null terminate */
                    178:     uni_pass[pass_len++] = 0;
                    179:     *uni_pass_len = pass_len;
                    180:     return uni_pass;
                    181: }
                    182: 
                    183: /*
                    184:  * Decrypt a pkcs8 block.
                    185:  */
                    186: static int p8_decrypt(const char *uni_pass, int uni_pass_len,
                    187:                         const uint8_t *salt, int iter, 
                    188:                         uint8_t *priv_key, int priv_key_len, int id)
                    189: {
                    190:     uint8_t p[BLOCK_SIZE*2];
                    191:     uint8_t d[BLOCK_SIZE];
                    192:     uint8_t Ai[SHA1_SIZE];
                    193:     SHA1_CTX sha_ctx;
                    194:     RC4_CTX rc4_ctx;
                    195:     int i;
                    196: 
                    197:     for (i = 0; i < BLOCK_SIZE; i++)
                    198:     {
                    199:         p[i] = salt[i % SALT_SIZE];
                    200:         p[BLOCK_SIZE+i] = uni_pass[i % uni_pass_len];
                    201:         d[i] = id;
                    202:     }
                    203: 
                    204:     /* get the key - no IV since we are using RC4 */
                    205:     SHA1_Init(&sha_ctx);
                    206:     SHA1_Update(&sha_ctx, d, sizeof(d));
                    207:     SHA1_Update(&sha_ctx, p, sizeof(p));
                    208:     SHA1_Final(Ai, &sha_ctx);
                    209: 
                    210:     for (i = 1; i < iter; i++)
                    211:     {
                    212:         SHA1_Init(&sha_ctx);
                    213:         SHA1_Update(&sha_ctx, Ai, SHA1_SIZE);
                    214:         SHA1_Final(Ai, &sha_ctx);
                    215:     }
                    216: 
                    217:     /* do the decryption */
                    218:     if (id == PKCS12_KEY_ID)
                    219:     {
                    220:         RC4_setup(&rc4_ctx, Ai, 16);
                    221:         RC4_crypt(&rc4_ctx, priv_key, priv_key, priv_key_len);
                    222:     }
                    223:     else  /* MAC */
                    224:         memcpy(priv_key, Ai, SHA1_SIZE);
                    225: 
                    226:     return 0;
                    227: }
                    228: 
                    229: /*
                    230:  * Take a raw pkcs12 block and the decrypt it and turn it into a certificate(s)
                    231:  * and keys.
                    232:  */
                    233: int pkcs12_decode(SSL_CTX *ssl_ctx, SSLObjLoader *ssl_obj, const char *password)
                    234: {
                    235:     uint8_t *buf = ssl_obj->buf;
                    236:     int len, iterations, auth_safes_start, 
                    237:               auth_safes_end, auth_safes_len, key_offset, offset = 0;
                    238:     int all_certs = 0;
                    239:     uint8_t *version = NULL, *auth_safes = NULL, *cert, *orig_mac;
                    240:     uint8_t key[SHA1_SIZE];
                    241:     uint8_t mac[SHA1_SIZE];
                    242:     const uint8_t *salt;
                    243:     int uni_pass_len, ret = SSL_OK;
                    244:     char *uni_pass = make_uni_pass(password, &uni_pass_len);
                    245:     static const uint8_t pkcs_data[] = /* pkc7 data */
                    246:         { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01 };
                    247:     static const uint8_t pkcs_encrypted[] = /* pkc7 encrypted */
                    248:         { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x06 };
                    249:     static const uint8_t pkcs8_key_bag[] = /* 1.2.840.113549.1.12.10.1.2 */
                    250:         { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x0a, 0x01, 0x02 };
                    251: 
                    252:     if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0)
                    253:     {
                    254: #ifdef CONFIG_SSL_FULL_MODE
                    255:         printf("Error: Invalid p12 ASN.1 file\n");
                    256: #endif
                    257:         goto error;
                    258:     }
                    259: 
                    260:     if (asn1_get_int(buf, &offset, &version) < 0 || *version != 3)
                    261:     {
                    262:         ret = SSL_ERROR_INVALID_VERSION;
                    263:         goto error;
                    264:     }
                    265: 
                    266:     /* remove all the boring pcks7 bits */
                    267:     if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || 
                    268:                 (len = asn1_next_obj(buf, &offset, ASN1_OID)) < 0 ||
                    269:                 len != sizeof(pkcs_data) || 
                    270:                 memcmp(&buf[offset], pkcs_data, sizeof(pkcs_data)))
                    271:         goto error;
                    272: 
                    273:     offset += len;
                    274: 
                    275:     if (asn1_next_obj(buf, &offset, ASN1_EXPLICIT_TAG) < 0 ||
                    276:             asn1_next_obj(buf, &offset, ASN1_OCTET_STRING) < 0)
                    277:         goto error;
                    278: 
                    279:     /* work out the MAC start/end points (done on AuthSafes) */
                    280:     auth_safes_start = offset;
                    281:     auth_safes_end = offset;
                    282:     if (asn1_skip_obj(buf, &auth_safes_end, ASN1_SEQUENCE) < 0)
                    283:         goto error;
                    284: 
                    285:     auth_safes_len = auth_safes_end - auth_safes_start;
                    286:     auth_safes = malloc(auth_safes_len);
                    287: 
                    288:     memcpy(auth_safes, &buf[auth_safes_start], auth_safes_len);
                    289: 
                    290:     if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
                    291:             asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
                    292:             (len = asn1_next_obj(buf, &offset, ASN1_OID)) < 0 ||
                    293:             (len != sizeof(pkcs_encrypted) || 
                    294:             memcmp(&buf[offset], pkcs_encrypted, sizeof(pkcs_encrypted))))
                    295:         goto error;
                    296: 
                    297:     offset += len;
                    298: 
                    299:     if (asn1_next_obj(buf, &offset, ASN1_EXPLICIT_TAG) < 0 ||
                    300:             asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
                    301:             asn1_skip_obj(buf, &offset, ASN1_INTEGER) < 0 ||
                    302:             asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
                    303:             (len = asn1_next_obj(buf, &offset, ASN1_OID)) < 0 ||
                    304:             len != sizeof(pkcs_data) || 
                    305:             memcmp(&buf[offset], pkcs_data, sizeof(pkcs_data)))
                    306:         goto error;
                    307: 
                    308:     offset += len;
                    309: 
                    310:     /* work out the salt for the certificate */
                    311:     if (get_pbe_params(buf, &offset, &salt, &iterations) < 0 ||
                    312:             (len = asn1_next_obj(buf, &offset, ASN1_IMPLICIT_TAG)) < 0)
                    313:         goto error;
                    314: 
                    315:     /* decrypt the certificate */
                    316:     cert = &buf[offset];
                    317:     if ((ret = p8_decrypt(uni_pass, uni_pass_len, salt, iterations, cert, 
                    318:                             len, PKCS12_KEY_ID)) < 0)
                    319:         goto error;
                    320: 
                    321:     offset += len;
                    322: 
                    323:     /* load the certificate */
                    324:     key_offset = 0;
                    325:     all_certs = asn1_next_obj(cert, &key_offset, ASN1_SEQUENCE);
                    326: 
                    327:     /* keep going until all certs are loaded */
                    328:     while (key_offset < all_certs)
                    329:     {
                    330:         int cert_offset = key_offset;
                    331: 
                    332:         if (asn1_skip_obj(cert, &cert_offset, ASN1_SEQUENCE) < 0 ||
                    333:                 asn1_next_obj(cert, &key_offset, ASN1_SEQUENCE) < 0 ||
                    334:                 asn1_skip_obj(cert, &key_offset, ASN1_OID) < 0 ||
                    335:                 asn1_next_obj(cert, &key_offset, ASN1_EXPLICIT_TAG) < 0 ||
                    336:                 asn1_next_obj(cert, &key_offset, ASN1_SEQUENCE) < 0 ||
                    337:                 asn1_skip_obj(cert, &key_offset, ASN1_OID) < 0 ||
                    338:                 asn1_next_obj(cert, &key_offset, ASN1_EXPLICIT_TAG) < 0 ||
                    339:                 (len = asn1_next_obj(cert, &key_offset, ASN1_OCTET_STRING)) < 0)
                    340:             goto error;
                    341: 
                    342:         if ((ret = add_cert(ssl_ctx, &cert[key_offset], len)) < 0)
                    343:             goto error;
                    344: 
                    345:         key_offset = cert_offset;
                    346:     }
                    347: 
                    348:     if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
                    349:             (len = asn1_next_obj(buf, &offset, ASN1_OID)) < 0 ||
                    350:             len != sizeof(pkcs_data) || 
                    351:             memcmp(&buf[offset], pkcs_data, sizeof(pkcs_data)))
                    352:         goto error;
                    353: 
                    354:     offset += len;
                    355: 
                    356:     if (asn1_next_obj(buf, &offset, ASN1_EXPLICIT_TAG) < 0 ||
                    357:             asn1_next_obj(buf, &offset, ASN1_OCTET_STRING) < 0 ||
                    358:             asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
                    359:             asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
                    360:             (len = asn1_next_obj(buf, &offset, ASN1_OID)) < 0 ||
                    361:             (len != sizeof(pkcs8_key_bag)) || 
                    362:             memcmp(&buf[offset], pkcs8_key_bag, sizeof(pkcs8_key_bag)))
                    363:         goto error;
                    364: 
                    365:     offset += len;
                    366: 
                    367:     /* work out the salt for the private key */
                    368:     if (asn1_next_obj(buf, &offset, ASN1_EXPLICIT_TAG) < 0 ||
                    369:             asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
                    370:             get_pbe_params(buf, &offset, &salt, &iterations) < 0 ||
                    371:             (len = asn1_next_obj(buf, &offset, ASN1_OCTET_STRING)) < 0)
                    372:         goto error;
                    373: 
                    374:     /* decrypt the private key */
                    375:     cert = &buf[offset];
                    376:     if ((ret = p8_decrypt(uni_pass, uni_pass_len, salt, iterations, cert, 
                    377:                             len, PKCS12_KEY_ID)) < 0)
                    378:         goto error;
                    379: 
                    380:     offset += len;
                    381: 
                    382:     /* load the private key */
                    383:     if ((ret = p8_add_key(ssl_ctx, cert)) < 0)
                    384:         goto error;
                    385: 
                    386:     /* miss out on friendly name, local key id etc */
                    387:     if (asn1_skip_obj(buf, &offset, ASN1_SET) < 0)
                    388:         goto error;
                    389: 
                    390:     /* work out the MAC */
                    391:     if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
                    392:             asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
                    393:             asn1_skip_obj(buf, &offset, ASN1_SEQUENCE) < 0 ||
                    394:             (len = asn1_next_obj(buf, &offset, ASN1_OCTET_STRING)) < 0 ||
                    395:             len != SHA1_SIZE)
                    396:         goto error;
                    397: 
                    398:     orig_mac = &buf[offset];
                    399:     offset += len;
                    400: 
                    401:     /* get the salt */
                    402:     if ((len = asn1_next_obj(buf, &offset, ASN1_OCTET_STRING)) < 0 || len != 8)
                    403:         goto error;
                    404: 
                    405:     salt = &buf[offset];
                    406: 
                    407:     /* work out what the mac should be */
                    408:     if ((ret = p8_decrypt(uni_pass, uni_pass_len, salt, iterations, 
                    409:                             key, SHA1_SIZE, PKCS12_MAC_ID)) < 0)
                    410:         goto error;
                    411: 
                    412:     hmac_sha1(auth_safes, auth_safes_len, key, SHA1_SIZE, mac);
                    413: 
                    414:     if (memcmp(mac, orig_mac, SHA1_SIZE))
                    415:     {
                    416:         ret = SSL_ERROR_INVALID_HMAC;                  
                    417:         goto error;
                    418:     }
                    419: 
                    420: error:
                    421:     free(version);
                    422:     free(uni_pass);
                    423:     free(auth_safes);
                    424:     return ret;
                    425: }
                    426: 
                    427: /*
                    428:  * Retrieve the salt/iteration details from a PBE block.
                    429:  */
                    430: static int get_pbe_params(uint8_t *buf, int *offset, 
                    431:         const uint8_t **salt, int *iterations)
                    432: {
                    433:     static const uint8_t pbeSH1RC4[] = /* pbeWithSHAAnd128BitRC4  */
                    434:             { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x01, 0x01 };
                    435: 
                    436:     int i, len;
                    437:     uint8_t *iter = NULL;
                    438:     int error_code = SSL_ERROR_NOT_SUPPORTED;
                    439: 
                    440:     /* Get the PBE type */
                    441:     if (asn1_next_obj(buf, offset, ASN1_SEQUENCE) < 0 ||
                    442:             (len = asn1_next_obj(buf, offset, ASN1_OID)) < 0)
                    443:         goto error;
                    444: 
                    445:     /* we expect pbeWithSHAAnd128BitRC4 (1.2.840.113549.1.12.1.1) 
                    446:        which is the only algorithm we support */
                    447:     if (len != sizeof(pbeSH1RC4) || 
                    448:                     memcmp(&buf[*offset], pbeSH1RC4, sizeof(pbeSH1RC4)))
                    449:     {
                    450: #ifdef CONFIG_SSL_FULL_MODE
                    451:         printf("Error: pkcs8/pkcs12 must use \"PBE-SHA1-RC4-128\"\n");
                    452: #endif
                    453:         goto error;
                    454:     }
                    455: 
                    456:     *offset += len;
                    457: 
                    458:     if (asn1_next_obj(buf, offset, ASN1_SEQUENCE) < 0 ||
                    459:             (len = asn1_next_obj(buf, offset, ASN1_OCTET_STRING)) < 0 || 
                    460:             len != 8)
                    461:         goto error;
                    462: 
                    463:     *salt = &buf[*offset];
                    464:     *offset += len;
                    465: 
                    466:     if ((len = asn1_get_int(buf, offset, &iter)) < 0)
                    467:         goto error;
                    468: 
                    469:     *iterations = 0;
                    470:     for (i = 0; i < len; i++)
                    471:     {
                    472:         (*iterations) <<= 8;
                    473:         (*iterations) += iter[i];
                    474:     }
                    475: 
                    476:     free(iter);
                    477:     error_code = SSL_OK;       /* got here - we are ok */
                    478: 
                    479: error:
                    480:     return error_code;
                    481: }
                    482: 
                    483: #endif

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>