1*4497207bSuwe /* $NetBSD: sign.c,v 1.9 2022/11/08 01:03:27 uwe Exp $ */
21c6aec20Schristos
31c6aec20Schristos /*-
41c6aec20Schristos * Copyright (c) 2008 The NetBSD Foundation, Inc.
51c6aec20Schristos * All rights reserved.
61c6aec20Schristos *
71c6aec20Schristos * This code is derived from software contributed to The NetBSD Foundation
81c6aec20Schristos * by Martin Sch�tte.
91c6aec20Schristos *
101c6aec20Schristos * Redistribution and use in source and binary forms, with or without
111c6aec20Schristos * modification, are permitted provided that the following conditions
121c6aec20Schristos * are met:
131c6aec20Schristos * 1. Redistributions of source code must retain the above copyright
141c6aec20Schristos * notice, this list of conditions and the following disclaimer.
151c6aec20Schristos * 2. Redistributions in binary form must reproduce the above copyright
161c6aec20Schristos * notice, this list of conditions and the following disclaimer in the
171c6aec20Schristos * documentation and/or other materials provided with the distribution.
181c6aec20Schristos * 3. All advertising materials mentioning features or use of this software
191c6aec20Schristos * must display the following acknowledgement:
201c6aec20Schristos * This product includes software developed by the NetBSD
211c6aec20Schristos * Foundation, Inc. and its contributors.
221c6aec20Schristos * 4. Neither the name of The NetBSD Foundation nor the names of its
231c6aec20Schristos * contributors may be used to endorse or promote products derived
241c6aec20Schristos * from this software without specific prior written permission.
251c6aec20Schristos *
261c6aec20Schristos * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
271c6aec20Schristos * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
281c6aec20Schristos * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
291c6aec20Schristos * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
301c6aec20Schristos * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
311c6aec20Schristos * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
321c6aec20Schristos * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
331c6aec20Schristos * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
341c6aec20Schristos * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
351c6aec20Schristos * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
361c6aec20Schristos * POSSIBILITY OF SUCH DAMAGE.
371c6aec20Schristos */
381c6aec20Schristos /*
391c6aec20Schristos * sign.c
401c6aec20Schristos * syslog-sign related code for syslogd
411c6aec20Schristos *
421c6aec20Schristos * Martin Sch�tte
431c6aec20Schristos */
441c6aec20Schristos /*
451c6aec20Schristos * Issues with the current internet draft:
461c6aec20Schristos * 1. The draft is a bit unclear on the input format for the signature,
471c6aec20Schristos * so this might have to be changed later. Cf. sign_string_sign()
481c6aec20Schristos * 2. The draft only defines DSA signatures. I hope it will be extended
491c6aec20Schristos * to DSS, thus allowing DSA, RSA (ANSI X9.31) and ECDSA (ANSI X9.62)
501c6aec20Schristos * 3. The draft does not define the data format for public keys in CBs.
511c6aec20Schristos * This implementation sends public keys in DER encoding.
521c6aec20Schristos * 4. This current implementation uses high-level OpenSSL API.
531c6aec20Schristos * I am not sure if these completely implement the FIPS/ANSI standards.
541c6aec20Schristos * Update after WG discussion in August:
551c6aec20Schristos * 1. check; next draft will be clearer and specify the format as implemented.
561c6aec20Schristos * 2. check; definitely only DSA in this version.
571c6aec20Schristos * 3. remains a problem, so far no statement from authors or WG.
584d91e676Schristos * 4. check; used EVP_sha1 method implements FIPS.
591c6aec20Schristos */
601c6aec20Schristos /*
611c6aec20Schristos * Limitations of this implementation:
621c6aec20Schristos * - cannot use OpenPGP keys, only PKIX or DSA due to OpenSSL capabilities
631c6aec20Schristos * - only works for correctly formatted messages, because incorrect messages
641c6aec20Schristos * are reformatted (e.g. if it receives a message with two spaces between
651c6aec20Schristos * fields it might even be parsed, but the output will have only one space).
661c6aec20Schristos */
671c6aec20Schristos
681c6aec20Schristos #include <sys/cdefs.h>
69*4497207bSuwe __RCSID("$NetBSD: sign.c,v 1.9 2022/11/08 01:03:27 uwe Exp $");
701c6aec20Schristos
711c6aec20Schristos #ifndef DISABLE_SIGN
721c6aec20Schristos #include "syslogd.h"
731c6aec20Schristos #ifndef DISABLE_TLS
741c6aec20Schristos #include "tls.h"
751c6aec20Schristos #endif /* !DISABLE_TLS */
761c6aec20Schristos #include "sign.h"
771c6aec20Schristos #include "extern.h"
781c6aec20Schristos
791c6aec20Schristos /*
801c6aec20Schristos * init all SGs for a given algorithm
811c6aec20Schristos */
821c6aec20Schristos bool
sign_global_init(struct filed * Files)831c6aec20Schristos sign_global_init(struct filed *Files)
841c6aec20Schristos {
851c6aec20Schristos DPRINTF((D_CALL|D_SIGN), "sign_global_init()\n");
861c6aec20Schristos if (!(GlobalSign.sg == 0 || GlobalSign.sg == 1
871c6aec20Schristos || GlobalSign.sg == 2 || GlobalSign.sg == 3)) {
881c6aec20Schristos logerror("sign_init(): invalid SG %d", GlobalSign.sg);
891c6aec20Schristos return false;
901c6aec20Schristos }
911c6aec20Schristos
921c6aec20Schristos if (!sign_get_keys())
931c6aec20Schristos return false;
941c6aec20Schristos
951c6aec20Schristos /* signature algorithm */
961c6aec20Schristos /* can probably be merged with the hash algorithm/context but
971c6aec20Schristos * I leave the optimization for later until the RFC is ready */
981c6aec20Schristos GlobalSign.sigctx = EVP_MD_CTX_create();
991c6aec20Schristos EVP_MD_CTX_init(GlobalSign.sigctx);
1001c6aec20Schristos
1011c6aec20Schristos /* the signature algorithm depends on the type of key */
1024d91e676Schristos switch (EVP_PKEY_base_id(GlobalSign.pubkey)) {
1034d91e676Schristos case EVP_PKEY_DSA:
1044d91e676Schristos GlobalSign.sig = EVP_sha1();
1051c6aec20Schristos GlobalSign.sig_len_b64 = SIGN_B64SIGLEN_DSS;
1064d91e676Schristos break;
1074d91e676Schristos #ifdef notyet
1084d91e676Schristos /* this is the place to add non-DSA key types and algorithms */
1094d91e676Schristos case EVP_PKEY_RSA:
1101c6aec20Schristos GlobalSign.sig = EVP_sha1();
1111c6aec20Schristos GlobalSign.sig_len_b64 = 28;
1124d91e676Schristos break;
1134d91e676Schristos #endif
1144d91e676Schristos default:
1151c6aec20Schristos logerror("key type not supported for syslog-sign");
1161c6aec20Schristos return false;
1171c6aec20Schristos }
1181c6aec20Schristos
1191c6aec20Schristos assert(GlobalSign.keytype == 'C' || GlobalSign.keytype == 'K');
1201c6aec20Schristos assert(GlobalSign.pubkey_b64 && GlobalSign.privkey &&
1211c6aec20Schristos GlobalSign.pubkey);
1221c6aec20Schristos
1231c6aec20Schristos GlobalSign.gbc = 0;
1241c6aec20Schristos STAILQ_INIT(&GlobalSign.SigGroups);
1251c6aec20Schristos
1261c6aec20Schristos /* hash algorithm */
1271c6aec20Schristos OpenSSL_add_all_digests();
1281c6aec20Schristos GlobalSign.mdctx = EVP_MD_CTX_create();
1291c6aec20Schristos EVP_MD_CTX_init(GlobalSign.mdctx);
1301c6aec20Schristos
1311c6aec20Schristos /* values for SHA-1 */
1324d91e676Schristos GlobalSign.md = EVP_sha1();
1331c6aec20Schristos GlobalSign.md_len_b64 = 28;
1341c6aec20Schristos GlobalSign.ver = "0111";
1351c6aec20Schristos
1361c6aec20Schristos if (!sign_sg_init(Files))
1371c6aec20Schristos return false;
1381c6aec20Schristos sign_new_reboot_session();
1391c6aec20Schristos
1401c6aec20Schristos DPRINTF(D_SIGN, "length values: SIGN_MAX_SD_LENGTH %d, "
1411c6aec20Schristos "SIGN_MAX_FRAG_LENGTH %d, SIGN_MAX_SB_LENGTH %d, "
1421c6aec20Schristos "SIGN_MAX_HASH_NUM %d\n", SIGN_MAX_SD_LENGTH,
1431c6aec20Schristos SIGN_MAX_FRAG_LENGTH, SIGN_MAX_SB_LENGTH, SIGN_MAX_HASH_NUM);
1441c6aec20Schristos
1451c6aec20Schristos /* set just before return, so it indicates initialization */
1461c6aec20Schristos GlobalSign.rsid = now;
1471c6aec20Schristos return true;
1481c6aec20Schristos }
1491c6aec20Schristos
1501c6aec20Schristos /*
1511c6aec20Schristos * get keys for syslog-sign
1521c6aec20Schristos * either from the X.509 certificate used for TLS
1531c6aec20Schristos * or by generating a new one
1541c6aec20Schristos *
1551c6aec20Schristos * sets the global variables
1561c6aec20Schristos * GlobalSign.keytype, GlobalSign.pubkey_b64,
1571c6aec20Schristos * GlobalSign.privkey, and GlobalSign.pubkey
1581c6aec20Schristos */
1591c6aec20Schristos bool
sign_get_keys(void)16092dd0698Schristos sign_get_keys(void)
1611c6aec20Schristos {
1621c6aec20Schristos EVP_PKEY *pubkey = NULL, *privkey = NULL;
1631c6aec20Schristos unsigned char *der_pubkey = NULL, *ptr_der_pubkey = NULL;
1641c6aec20Schristos char *pubkey_b64 = NULL;
1651c6aec20Schristos int der_len;
1661c6aec20Schristos
1671c6aec20Schristos /* try PKIX/TLS key first */
1681c6aec20Schristos #ifndef DISABLE_TLS
1691c6aec20Schristos SSL *ssl;
1701c6aec20Schristos if (tls_opt.global_TLS_CTX
1711c6aec20Schristos && (ssl = SSL_new(tls_opt.global_TLS_CTX))) {
1721c6aec20Schristos X509 *cert;
1731c6aec20Schristos DPRINTF(D_SIGN, "Try to get keys from TLS X.509 cert...\n");
1741c6aec20Schristos
1751c6aec20Schristos if (!(cert = SSL_get_certificate(ssl))) {
1761c6aec20Schristos logerror("SSL_get_certificate() failed");
1771c6aec20Schristos FREE_SSL(ssl);
1781c6aec20Schristos return false;
1791c6aec20Schristos }
1801c6aec20Schristos if (!(privkey = SSL_get_privatekey(ssl))) {
1811c6aec20Schristos logerror("SSL_get_privatekey() failed");
1821c6aec20Schristos FREE_SSL(ssl);
1831c6aec20Schristos return false;
1841c6aec20Schristos }
1851c6aec20Schristos if (!(pubkey = X509_get_pubkey(cert))) {
1861c6aec20Schristos logerror("X509_get_pubkey() failed");
1871c6aec20Schristos FREE_SSL(ssl);
1881c6aec20Schristos return false;
1891c6aec20Schristos }
1901c6aec20Schristos /* note:
1911c6aec20Schristos * - privkey is just a pointer into SSL_CTX and
1921c6aec20Schristos * must not be changed nor be free()d
1931c6aec20Schristos * - but pubkey has to be freed with EVP_PKEY_free()
1941c6aec20Schristos */
1951c6aec20Schristos FREE_SSL(ssl);
1961c6aec20Schristos
1974d91e676Schristos if (EVP_PKEY_DSA != EVP_PKEY_base_id(pubkey)) {
1981c6aec20Schristos DPRINTF(D_SIGN, "X.509 cert has no DSA key\n");
1991c6aec20Schristos EVP_PKEY_free(pubkey);
2001c6aec20Schristos privkey = NULL;
2011c6aec20Schristos pubkey = NULL;
2021c6aec20Schristos } else {
2031c6aec20Schristos DPRINTF(D_SIGN, "Got public and private key "
2041c6aec20Schristos "from X.509 --> use type PKIX\n");
2051c6aec20Schristos GlobalSign.keytype = 'C';
2061c6aec20Schristos GlobalSign.privkey = privkey;
2071c6aec20Schristos GlobalSign.pubkey = pubkey;
2081c6aec20Schristos
2091c6aec20Schristos /* base64 certificate encoding */
2101c6aec20Schristos der_len = i2d_X509(cert, NULL);
2111c6aec20Schristos if (!(ptr_der_pubkey = der_pubkey = malloc(der_len))
2121c6aec20Schristos || !(pubkey_b64 = malloc(der_len*2))) {
2131c6aec20Schristos free(der_pubkey);
2141c6aec20Schristos logerror("malloc() failed");
2151c6aec20Schristos return false;
2161c6aec20Schristos }
2171c6aec20Schristos if (i2d_X509(cert, &ptr_der_pubkey) <= 0) {
2181c6aec20Schristos logerror("i2d_X509() failed");
2191c6aec20Schristos return false;
2201c6aec20Schristos }
2211c6aec20Schristos b64_ntop(der_pubkey, der_len, pubkey_b64, der_len*2);
2221c6aec20Schristos free(der_pubkey);
2231c6aec20Schristos /* try to resize memory object as needed */
2241c6aec20Schristos GlobalSign.pubkey_b64 = realloc(pubkey_b64,
2251c6aec20Schristos strlen(pubkey_b64)+1);
2261c6aec20Schristos if (!GlobalSign.pubkey_b64)
2271c6aec20Schristos GlobalSign.pubkey_b64 = pubkey_b64;
2281c6aec20Schristos }
2291c6aec20Schristos }
2301c6aec20Schristos #endif /* !DISABLE_TLS */
2311c6aec20Schristos if (!(privkey && pubkey)) { /* PKIX not available --> generate key */
2321c6aec20Schristos DSA *dsa;
2331c6aec20Schristos
2341c6aec20Schristos DPRINTF(D_SIGN, "Unable to get keys from X.509 "
2351c6aec20Schristos "--> use DSA with type 'K'\n");
2361c6aec20Schristos if (!(privkey = EVP_PKEY_new())) {
2371c6aec20Schristos logerror("EVP_PKEY_new() failed");
2381c6aec20Schristos return false;
2391c6aec20Schristos }
2404d91e676Schristos if ((dsa = DSA_new()) == NULL) {
2414d91e676Schristos logerror("DSA_new() failed");
2424d91e676Schristos return false;
2434d91e676Schristos }
2444d91e676Schristos if (!DSA_generate_parameters_ex(dsa, SIGN_GENCERT_BITS, NULL, 0,
2454d91e676Schristos NULL, NULL, NULL)) {
2464d91e676Schristos logerror("DSA_generate_parameters_ex() failed");
2474d91e676Schristos return false;
2484d91e676Schristos }
2491c6aec20Schristos if (!DSA_generate_key(dsa)) {
2501c6aec20Schristos logerror("DSA_generate_key() failed");
2511c6aec20Schristos return false;
2521c6aec20Schristos }
2531c6aec20Schristos if (!EVP_PKEY_assign_DSA(privkey, dsa)) {
2541c6aec20Schristos logerror("EVP_PKEY_assign_DSA() failed");
2551c6aec20Schristos return false;
2561c6aec20Schristos }
2571c6aec20Schristos GlobalSign.keytype = 'K'; /* public/private keys used */
2581c6aec20Schristos GlobalSign.privkey = privkey;
2591c6aec20Schristos GlobalSign.pubkey = privkey;
2601c6aec20Schristos
2611c6aec20Schristos /* pubkey base64 encoding */
2621c6aec20Schristos der_len = i2d_DSA_PUBKEY(dsa, NULL);
2631c6aec20Schristos if (!(ptr_der_pubkey = der_pubkey = malloc(der_len))
2641c6aec20Schristos || !(pubkey_b64 = malloc(der_len*2))) {
2651c6aec20Schristos free(der_pubkey);
2661c6aec20Schristos logerror("malloc() failed");
2671c6aec20Schristos return false;
2681c6aec20Schristos }
2691c6aec20Schristos if (i2d_DSA_PUBKEY(dsa, &ptr_der_pubkey) <= 0) {
2701c6aec20Schristos logerror("i2d_DSA_PUBKEY() failed");
2715091f368Sspz free(der_pubkey);
2725091f368Sspz free(pubkey_b64);
2731c6aec20Schristos return false;
2741c6aec20Schristos }
2751c6aec20Schristos b64_ntop(der_pubkey, der_len, pubkey_b64, der_len*2);
2761c6aec20Schristos free(der_pubkey);
2771c6aec20Schristos /* try to resize memory object as needed */
2781c6aec20Schristos GlobalSign.pubkey_b64 = realloc(pubkey_b64,
2791c6aec20Schristos strlen(pubkey_b64) + 1);
2801c6aec20Schristos if (!GlobalSign.pubkey_b64)
2811c6aec20Schristos GlobalSign.pubkey_b64 = pubkey_b64;
2821c6aec20Schristos }
2831c6aec20Schristos return true;
2841c6aec20Schristos }
2851c6aec20Schristos
2861c6aec20Schristos /*
2871c6aec20Schristos * init SGs
2881c6aec20Schristos */
2891c6aec20Schristos bool
sign_sg_init(struct filed * Files)2901c6aec20Schristos sign_sg_init(struct filed *Files)
2911c6aec20Schristos {
2921c6aec20Schristos struct signature_group_t *sg, *newsg, *last_sg;
2931c6aec20Schristos struct filed_queue *fq;
2941c6aec20Schristos struct string_queue *sqentry, *last_sqentry;
2951c6aec20Schristos struct filed *f;
2967e0b2295Slukem unsigned int i;
2971c6aec20Schristos
2981c6aec20Schristos /* note on SG 1 and 2:
2991c6aec20Schristos * it is assumed that redundant signature groups
3001c6aec20Schristos * and especially signature groups without an associated
3011c6aec20Schristos * destination are harmless.
3021c6aec20Schristos * this currently holds true because sign_append_hash()
3031c6aec20Schristos * is called from fprintlog(), so only actually used
3041c6aec20Schristos * signature group get hashes and need memory for them
3051c6aec20Schristos */
3061c6aec20Schristos /* possible optimization for SGs 1 and 2:
3071c6aec20Schristos * use a struct signature_group_t *newsg[IETF_NUM_PRIVALUES]
3081c6aec20Schristos * for direct group lookup
3091c6aec20Schristos */
3101c6aec20Schristos
3111c6aec20Schristos #define ALLOC_OR_FALSE(x) do { \
3121c6aec20Schristos if(!((x) = calloc(1, sizeof(*(x))))) { \
3131c6aec20Schristos logerror("Unable to allocate memory"); \
3141c6aec20Schristos return false; \
3151c6aec20Schristos } \
3166f4965e0Srillig } while (0)
3171c6aec20Schristos
3181c6aec20Schristos #define ALLOC_SG(x) do { \
3191c6aec20Schristos ALLOC_OR_FALSE(x); \
3201c6aec20Schristos (x)->last_msg_num = 1; /* cf. section 4.2.5 */ \
3211c6aec20Schristos STAILQ_INIT(&(x)->hashes); \
3221c6aec20Schristos STAILQ_INIT(&(x)->files); \
3236f4965e0Srillig } while (0)
3241c6aec20Schristos
3251c6aec20Schristos /* alloc(fq) and add to SGs file queue */
3261c6aec20Schristos #define ASSIGN_FQ() do { \
3271c6aec20Schristos ALLOC_OR_FALSE(fq); \
3281c6aec20Schristos fq->f = f; \
3291c6aec20Schristos f->f_sg = newsg; \
3301c6aec20Schristos DPRINTF(D_SIGN, "SG@%p <--> f@%p\n", newsg, f); \
3311c6aec20Schristos STAILQ_INSERT_TAIL(&newsg->files, fq, entries); \
3326f4965e0Srillig } while (0)
3331c6aec20Schristos
3341c6aec20Schristos switch (GlobalSign.sg) {
3351c6aec20Schristos case 0:
3361c6aec20Schristos /* one SG, linked to all files */
3371c6aec20Schristos ALLOC_SG(newsg);
3381c6aec20Schristos newsg->spri = 0;
3391c6aec20Schristos for (f = Files; f; f = f->f_next)
3401c6aec20Schristos ASSIGN_FQ();
3411c6aec20Schristos STAILQ_INSERT_TAIL(&GlobalSign.SigGroups,
3421c6aec20Schristos newsg, entries);
3431c6aec20Schristos break;
3441c6aec20Schristos case 1:
3451c6aec20Schristos /* every PRI gets one SG */
3461c6aec20Schristos for (i = 0; i < IETF_NUM_PRIVALUES; i++) {
3471c6aec20Schristos int fac, prilev;
3481c6aec20Schristos fac = LOG_FAC(i);
3491c6aec20Schristos prilev = LOG_PRI(i);
3501c6aec20Schristos ALLOC_SG(newsg);
3511c6aec20Schristos newsg->spri = i;
3521c6aec20Schristos
3531c6aec20Schristos /* now find all destinations associated with this SG */
3541c6aec20Schristos for (f = Files; f; f = f->f_next)
3551c6aec20Schristos /* check priorities */
3561c6aec20Schristos if (MATCH_PRI(f, fac, prilev))
3571c6aec20Schristos ASSIGN_FQ();
3581c6aec20Schristos STAILQ_INSERT_TAIL(&GlobalSign.SigGroups,
3591c6aec20Schristos newsg, entries);
3601c6aec20Schristos }
3611c6aec20Schristos break;
3621c6aec20Schristos case 2:
3631c6aec20Schristos /* PRI ranges get one SG, boundaries given by the
3641c6aec20Schristos * SPRI, indicating the largest PRI in the SG
3651c6aec20Schristos *
3661c6aec20Schristos * either GlobalSign.sig2_delims has a list of
3671c6aec20Schristos * user configured delimiters, or we use a default
3681c6aec20Schristos * and set up one SG per facility
3691c6aec20Schristos */
3701c6aec20Schristos if (STAILQ_EMPTY(&GlobalSign.sig2_delims)) {
3711c6aec20Schristos DPRINTF(D_SIGN, "sign_sg_init(): set default "
3721c6aec20Schristos "values for SG 2\n");
3731c6aec20Schristos for (i = 0; i < (IETF_NUM_PRIVALUES>>3); i++) {
3741c6aec20Schristos ALLOC_OR_FALSE(sqentry);
3751c6aec20Schristos sqentry->data = NULL;
3761c6aec20Schristos sqentry->key = (i<<3);
3771c6aec20Schristos STAILQ_INSERT_TAIL(&GlobalSign.sig2_delims,
3781c6aec20Schristos sqentry, entries);
3791c6aec20Schristos }
3801c6aec20Schristos }
3811c6aec20Schristos assert(!STAILQ_EMPTY(&GlobalSign.sig2_delims));
3821c6aec20Schristos
3831c6aec20Schristos /* add one more group at the end */
3841c6aec20Schristos last_sqentry = STAILQ_LAST(&GlobalSign.sig2_delims,
3851c6aec20Schristos string_queue, entries);
3861c6aec20Schristos if (last_sqentry->key < IETF_NUM_PRIVALUES) {
3871c6aec20Schristos ALLOC_OR_FALSE(sqentry);
3881c6aec20Schristos sqentry->data = NULL;
3891c6aec20Schristos sqentry->key = IETF_NUM_PRIVALUES-1;
3901c6aec20Schristos STAILQ_INSERT_TAIL(&GlobalSign.sig2_delims,
3911c6aec20Schristos sqentry, entries);
3921c6aec20Schristos }
3931c6aec20Schristos
3941c6aec20Schristos STAILQ_FOREACH(sqentry, &GlobalSign.sig2_delims, entries) {
3957e0b2295Slukem unsigned int min_pri = 0;
3961c6aec20Schristos ALLOC_SG(newsg);
3971c6aec20Schristos newsg->spri = sqentry->key;
3981c6aec20Schristos
3991c6aec20Schristos /* check _all_ priorities in SG */
4001c6aec20Schristos last_sg = STAILQ_LAST(&GlobalSign.SigGroups,
4011c6aec20Schristos signature_group_t, entries);
4021c6aec20Schristos if (last_sg)
4031c6aec20Schristos min_pri = last_sg->spri + 1;
4041c6aec20Schristos
4051c6aec20Schristos DPRINTF(D_SIGN, "sign_sg_init(): add SG@%p: SG=\"2\","
4061c6aec20Schristos " SPRI=\"%d\" -- for msgs with "
4071c6aec20Schristos "%d <= pri <= %d\n",
4081c6aec20Schristos newsg, newsg->spri, min_pri, newsg->spri);
4091c6aec20Schristos /* now find all destinations associated with this SG */
4101c6aec20Schristos for (f = Files; f; f = f->f_next) {
4111c6aec20Schristos bool match = false;
4121c6aec20Schristos for (i = min_pri; i <= newsg->spri; i++) {
4131c6aec20Schristos int fac, prilev;
4141c6aec20Schristos fac = LOG_FAC(i);
4151c6aec20Schristos prilev = LOG_PRI(i);
4161c6aec20Schristos if (MATCH_PRI(f, fac, prilev)) {
4171c6aec20Schristos match = true;
4181c6aec20Schristos break;
4191c6aec20Schristos }
4201c6aec20Schristos }
4211c6aec20Schristos if (match)
4221c6aec20Schristos ASSIGN_FQ();
4231c6aec20Schristos }
4241c6aec20Schristos STAILQ_INSERT_TAIL(&GlobalSign.SigGroups,
4251c6aec20Schristos newsg, entries);
4261c6aec20Schristos }
4271c6aec20Schristos break;
4281c6aec20Schristos case 3:
4291c6aec20Schristos /* every file (with flag) gets one SG */
4301c6aec20Schristos for (f = Files; f; f = f->f_next) {
4311c6aec20Schristos if (!(f->f_flags & FFLAG_SIGN)) {
4321c6aec20Schristos f->f_sg = NULL;
4331c6aec20Schristos continue;
4341c6aec20Schristos }
4351c6aec20Schristos ALLOC_SG(newsg);
4361c6aec20Schristos newsg->spri = f->f_file; /* not needed but shows SGs */
4371c6aec20Schristos ASSIGN_FQ();
4381c6aec20Schristos STAILQ_INSERT_TAIL(&GlobalSign.SigGroups,
4391c6aec20Schristos newsg, entries);
4401c6aec20Schristos }
4411c6aec20Schristos break;
4421c6aec20Schristos }
4431c6aec20Schristos DPRINTF((D_PARSE|D_SIGN), "sign_sg_init() set up these "
4441c6aec20Schristos "Signature Groups:\n");
4451c6aec20Schristos STAILQ_FOREACH(sg, &GlobalSign.SigGroups, entries) {
4461c6aec20Schristos DPRINTF((D_PARSE|D_SIGN), "SG@%p with SG=\"%d\", SPRI=\"%d\","
4471c6aec20Schristos " associated files:\n", sg, GlobalSign.sg, sg->spri);
4481c6aec20Schristos STAILQ_FOREACH(fq, &sg->files, entries) {
4491c6aec20Schristos DPRINTF((D_PARSE|D_SIGN), " f@%p with type %d\n",
4501c6aec20Schristos fq->f, fq->f->f_type);
4511c6aec20Schristos }
4521c6aec20Schristos }
4531c6aec20Schristos return true;
4541c6aec20Schristos }
4551c6aec20Schristos
4561c6aec20Schristos /*
4571c6aec20Schristos * free all SGs for a given algorithm
4581c6aec20Schristos */
4591c6aec20Schristos void
sign_global_free(void)46092dd0698Schristos sign_global_free(void)
4611c6aec20Schristos {
4621c6aec20Schristos struct signature_group_t *sg, *tmp_sg;
4631c6aec20Schristos struct filed_queue *fq, *tmp_fq;
4641c6aec20Schristos
4651c6aec20Schristos DPRINTF((D_CALL|D_SIGN), "sign_global_free()\n");
4661c6aec20Schristos STAILQ_FOREACH_SAFE(sg, &GlobalSign.SigGroups, entries, tmp_sg) {
4671c6aec20Schristos if (!STAILQ_EMPTY(&sg->hashes)) {
4681c6aec20Schristos /* send CB and SB twice to get minimal redundancy
4691c6aec20Schristos * for the last few message hashes */
4701c6aec20Schristos sign_send_certificate_block(sg);
4711c6aec20Schristos sign_send_certificate_block(sg);
4721c6aec20Schristos sign_send_signature_block(sg, true);
4731c6aec20Schristos sign_send_signature_block(sg, true);
4741c6aec20Schristos sign_free_hashes(sg);
4751c6aec20Schristos }
4761c6aec20Schristos fq = STAILQ_FIRST(&sg->files);
4771c6aec20Schristos while (fq != NULL) {
4781c6aec20Schristos tmp_fq = STAILQ_NEXT(fq, entries);
4791c6aec20Schristos free(fq);
4801c6aec20Schristos fq = tmp_fq;
4811c6aec20Schristos }
4821c6aec20Schristos STAILQ_REMOVE(&GlobalSign.SigGroups,
4831c6aec20Schristos sg, signature_group_t, entries);
4841c6aec20Schristos free(sg);
4851c6aec20Schristos }
4861c6aec20Schristos sign_free_string_queue(&GlobalSign.sig2_delims);
4871c6aec20Schristos
4881c6aec20Schristos if (GlobalSign.privkey) {
4891c6aec20Schristos GlobalSign.privkey = NULL;
4901c6aec20Schristos }
4911c6aec20Schristos if (GlobalSign.pubkey) {
4921c6aec20Schristos EVP_PKEY_free(GlobalSign.pubkey);
4931c6aec20Schristos GlobalSign.pubkey = NULL;
4941c6aec20Schristos }
4951c6aec20Schristos if(GlobalSign.mdctx) {
4961c6aec20Schristos EVP_MD_CTX_destroy(GlobalSign.mdctx);
4971c6aec20Schristos GlobalSign.mdctx = NULL;
4981c6aec20Schristos }
4991c6aec20Schristos if(GlobalSign.sigctx) {
5001c6aec20Schristos EVP_MD_CTX_destroy(GlobalSign.sigctx);
5011c6aec20Schristos GlobalSign.sigctx = NULL;
5021c6aec20Schristos }
5031c6aec20Schristos FREEPTR(GlobalSign.pubkey_b64);
5041c6aec20Schristos }
5051c6aec20Schristos
5061c6aec20Schristos /*
5071c6aec20Schristos * create and send certificate block
5081c6aec20Schristos */
5091c6aec20Schristos bool
sign_send_certificate_block(struct signature_group_t * sg)5101c6aec20Schristos sign_send_certificate_block(struct signature_group_t *sg)
5111c6aec20Schristos {
5121c6aec20Schristos struct filed_queue *fq;
5131c6aec20Schristos struct buf_msg *buffer;
5141c6aec20Schristos char *tstamp;
5151c6aec20Schristos char payload[SIGN_MAX_PAYLOAD_LENGTH];
5161c6aec20Schristos char sd[SIGN_MAX_SD_LENGTH];
517*4497207bSuwe size_t payload_len, fragment_len;
5181c6aec20Schristos size_t payload_index = 0;
5191c6aec20Schristos
5201c6aec20Schristos /* do nothing if CBs already sent or if there was no message in SG */
5211c6aec20Schristos if (!sg->resendcount
5221c6aec20Schristos || ((sg->resendcount == SIGN_RESENDCOUNT_CERTBLOCK)
5231c6aec20Schristos && STAILQ_EMPTY(&sg->hashes)))
5241c6aec20Schristos return false;
5251c6aec20Schristos
5261c6aec20Schristos DPRINTF((D_CALL|D_SIGN), "sign_send_certificate_block(%p)\n", sg);
5277779d774Schristos tstamp = make_timestamp(NULL, true, (size_t)-1);
5281c6aec20Schristos
5291c6aec20Schristos payload_len = snprintf(payload, sizeof(payload), "%s %c %s", tstamp,
5301c6aec20Schristos GlobalSign.keytype, GlobalSign.pubkey_b64);
5311c6aec20Schristos if (payload_len >= sizeof(payload)) {
5321c6aec20Schristos DPRINTF(D_SIGN, "Buffer too small for syslog-sign setup\n");
5331c6aec20Schristos return false;
5341c6aec20Schristos }
5351c6aec20Schristos
5361c6aec20Schristos while (payload_index < payload_len) {
5371c6aec20Schristos if (payload_len - payload_index <= SIGN_MAX_FRAG_LENGTH)
5381c6aec20Schristos fragment_len = payload_len - payload_index;
5391c6aec20Schristos else
5401c6aec20Schristos fragment_len = SIGN_MAX_FRAG_LENGTH;
5411c6aec20Schristos
5421c6aec20Schristos /* format SD */
543*4497207bSuwe size_t sd_len __diagused;
5441c6aec20Schristos sd_len = snprintf(sd, sizeof(sd), "[ssign-cert "
5451c6aec20Schristos "VER=\"%s\" RSID=\"%" PRIuFAST64 "\" SG=\"%d\" "
5461c6aec20Schristos "SPRI=\"%d\" TBPL=\"%zu\" INDEX=\"%zu\" "
5471c6aec20Schristos "FLEN=\"%zu\" FRAG=\"%.*s\" "
5481c6aec20Schristos "SIGN=\"\"]",
5491c6aec20Schristos GlobalSign.ver, GlobalSign.rsid, GlobalSign.sg,
5501c6aec20Schristos sg->spri, payload_len, payload_index+1,
5511c6aec20Schristos fragment_len, (int)fragment_len,
5521c6aec20Schristos &payload[payload_index]);
5531c6aec20Schristos assert(sd_len < sizeof(sd));
5541c6aec20Schristos assert(sd[sd_len] == '\0');
5551c6aec20Schristos assert(sd[sd_len-1] == ']');
5561c6aec20Schristos assert(sd[sd_len-2] == '"');
5571c6aec20Schristos
5581c6aec20Schristos if (!sign_msg_sign(&buffer, sd, sizeof(sd)))
5591c6aec20Schristos return 0;
5601c6aec20Schristos DPRINTF((D_CALL|D_SIGN), "sign_send_certificate_block(): "
5611c6aec20Schristos "calling fprintlog()\n");
5621c6aec20Schristos
5631c6aec20Schristos STAILQ_FOREACH(fq, &sg->files, entries) {
5641c6aec20Schristos /* we have to preserve the f_prevcount */
5651c6aec20Schristos int tmpcnt;
5661c6aec20Schristos tmpcnt = fq->f->f_prevcount;
5671c6aec20Schristos fprintlog(fq->f, buffer, NULL);
5681c6aec20Schristos fq->f->f_prevcount = tmpcnt;
5691c6aec20Schristos }
5701c6aec20Schristos sign_inc_gbc();
5711c6aec20Schristos DELREF(buffer);
5721c6aec20Schristos payload_index += fragment_len;
5731c6aec20Schristos }
5741c6aec20Schristos sg->resendcount--;
5751c6aec20Schristos return true;
5761c6aec20Schristos }
5771c6aec20Schristos
5781c6aec20Schristos /*
5791c6aec20Schristos * determine the SG for a message
5801c6aec20Schristos * returns NULL if -sign not configured or no SG for this priority
5811c6aec20Schristos */
5821c6aec20Schristos struct signature_group_t *
sign_get_sg(int pri,struct filed * f)5831c6aec20Schristos sign_get_sg(int pri, struct filed *f)
5841c6aec20Schristos {
5851c6aec20Schristos struct signature_group_t *sg, *rc = NULL;
5861c6aec20Schristos
5871c6aec20Schristos if (GlobalSign.rsid && f)
5881c6aec20Schristos switch (GlobalSign.sg) {
5891c6aec20Schristos case 0:
5901c6aec20Schristos rc = f->f_sg;
5911c6aec20Schristos break;
5921c6aec20Schristos case 1:
5931c6aec20Schristos case 2:
5941c6aec20Schristos STAILQ_FOREACH(sg, &GlobalSign.SigGroups, entries) {
5957e0b2295Slukem if (sg->spri >= (unsigned int)pri) {
5961c6aec20Schristos rc = sg;
5971c6aec20Schristos break;
5981c6aec20Schristos }
5991c6aec20Schristos }
6001c6aec20Schristos break;
6011c6aec20Schristos case 3:
6021c6aec20Schristos if (f->f_flags & FFLAG_SIGN)
6031c6aec20Schristos rc = f->f_sg;
6041c6aec20Schristos else
6051c6aec20Schristos rc = NULL;
6061c6aec20Schristos break;
6071c6aec20Schristos }
6081c6aec20Schristos
6091c6aec20Schristos DPRINTF((D_CALL|D_SIGN), "sign_get_sg(%d, %p) --> %p\n", pri, f, rc);
6101c6aec20Schristos return rc;
6111c6aec20Schristos }
6121c6aec20Schristos
6131c6aec20Schristos /*
6141c6aec20Schristos * create and send signature block
6151c6aec20Schristos *
6161c6aec20Schristos * uses a sliding window for redundancy
6171c6aec20Schristos * if force==true then simply send all available hashes, e.g. on shutdown
6181c6aec20Schristos *
6191c6aec20Schristos * sliding window checks implicitly assume that new hashes are appended
6201c6aec20Schristos * to the SG between two calls. if that is not the case (e.g. with repeated
6211c6aec20Schristos * messages) the queue size will shrink.
6221c6aec20Schristos * this has no negative consequences except generating more and shorter SBs
6231c6aec20Schristos * than expected and confusing the operator because two consecutive SBs will
6241c6aec20Schristos * have same FMNn
6251c6aec20Schristos */
6261c6aec20Schristos unsigned
sign_send_signature_block(struct signature_group_t * sg,bool force)6271c6aec20Schristos sign_send_signature_block(struct signature_group_t *sg, bool force)
6281c6aec20Schristos {
6291c6aec20Schristos char sd[SIGN_MAX_SD_LENGTH];
6301c6aec20Schristos size_t sd_len;
6311c6aec20Schristos size_t sg_num_hashes = 0; /* hashes in SG queue */
6321c6aec20Schristos size_t hashes_in_sb = 0; /* number of hashes in current SB */
6331c6aec20Schristos size_t hashes_sent = 0; /* count of hashes sent */
6341c6aec20Schristos struct string_queue *qentry, *old_qentry;
6351c6aec20Schristos struct buf_msg *buffer;
6361c6aec20Schristos struct filed_queue *fq;
6377e0b2295Slukem size_t i;
6381c6aec20Schristos
6391c6aec20Schristos if (!sg) return 0;
6401c6aec20Schristos DPRINTF((D_CALL|D_SIGN), "sign_send_signature_block(%p, %d)\n",
6411c6aec20Schristos sg, force);
6421c6aec20Schristos
6431c6aec20Schristos STAILQ_FOREACH(qentry, &sg->hashes, entries)
6441c6aec20Schristos sg_num_hashes++;
6451c6aec20Schristos
6461c6aec20Schristos /* only act if a division is full */
6471c6aec20Schristos if (!sg_num_hashes
6481c6aec20Schristos || (!force && (sg_num_hashes % SIGN_HASH_DIVISION_NUM)))
6491c6aec20Schristos return 0;
6501c6aec20Schristos
6511c6aec20Schristos /* if no CB sent so far then do now, just before first SB */
6521c6aec20Schristos if (sg->resendcount == SIGN_RESENDCOUNT_CERTBLOCK)
6531c6aec20Schristos sign_send_certificate_block(sg);
6541c6aec20Schristos
6551c6aec20Schristos /* shortly after reboot we have shorter SBs */
6561c6aec20Schristos hashes_in_sb = MIN(sg_num_hashes, SIGN_HASH_NUM);
6571c6aec20Schristos
6581c6aec20Schristos DPRINTF(D_SIGN, "sign_send_signature_block(): "
6591c6aec20Schristos "sg_num_hashes = %zu, hashes_in_sb = %zu, SIGN_HASH_NUM = %d\n",
6601c6aec20Schristos sg_num_hashes, hashes_in_sb, SIGN_HASH_NUM);
6611c6aec20Schristos if (sg_num_hashes > SIGN_HASH_NUM) {
6621c6aec20Schristos DPRINTF(D_SIGN, "sign_send_signature_block(): sg_num_hashes"
6631c6aec20Schristos " > SIGN_HASH_NUM -- This should not happen!\n");
6641c6aec20Schristos }
6651c6aec20Schristos
6661c6aec20Schristos /* now the SD */
6671c6aec20Schristos qentry = STAILQ_FIRST(&sg->hashes);
6681c6aec20Schristos sd_len = snprintf(sd, sizeof(sd), "[ssign "
6691c6aec20Schristos "VER=\"%s\" RSID=\"%" PRIuFAST64 "\" SG=\"%d\" "
6701c6aec20Schristos "SPRI=\"%d\" GBC=\"%" PRIuFAST64 "\" FMN=\"%" PRIuFAST64 "\" "
6711c6aec20Schristos "CNT=\"%zu\" HB=\"",
6721c6aec20Schristos GlobalSign.ver, GlobalSign.rsid, GlobalSign.sg,
6731c6aec20Schristos sg->spri, GlobalSign.gbc, qentry->key,
6741c6aec20Schristos hashes_in_sb);
6751c6aec20Schristos while (hashes_sent < hashes_in_sb) {
6761c6aec20Schristos assert(qentry);
6771c6aec20Schristos sd_len += snprintf(sd+sd_len, sizeof(sd)-sd_len, "%s ",
6781c6aec20Schristos qentry->data);
6791c6aec20Schristos hashes_sent++;
6801c6aec20Schristos qentry = STAILQ_NEXT(qentry, entries);
6811c6aec20Schristos }
6821c6aec20Schristos /* overwrite last space and close SD */
6831c6aec20Schristos assert(sd_len < sizeof(sd));
6841c6aec20Schristos assert(sd[sd_len] == '\0');
6851c6aec20Schristos assert(sd[sd_len-1] == ' ');
6861c6aec20Schristos sd[sd_len-1] = '\0';
6871c6aec20Schristos sd_len = strlcat(sd, "\" SIGN=\"\"]", sizeof(sd));
6881c6aec20Schristos
6891c6aec20Schristos if (sign_msg_sign(&buffer, sd, sizeof(sd))) {
6901c6aec20Schristos DPRINTF((D_CALL|D_SIGN), "sign_send_signature_block(): calling"
6911c6aec20Schristos " fprintlog(), sending %zu out of %zu hashes\n",
6921c6aec20Schristos MIN(SIGN_MAX_HASH_NUM, sg_num_hashes), sg_num_hashes);
6931c6aec20Schristos
6941c6aec20Schristos STAILQ_FOREACH(fq, &sg->files, entries) {
6951c6aec20Schristos int tmpcnt;
6961c6aec20Schristos tmpcnt = fq->f->f_prevcount;
6971c6aec20Schristos fprintlog(fq->f, buffer, NULL);
6981c6aec20Schristos fq->f->f_prevcount = tmpcnt;
6991c6aec20Schristos }
7001c6aec20Schristos sign_inc_gbc();
7011c6aec20Schristos DELREF(buffer);
7021c6aec20Schristos }
7031c6aec20Schristos /* always drop the oldest division of hashes */
7041c6aec20Schristos if (sg_num_hashes >= SIGN_HASH_NUM) {
7051c6aec20Schristos qentry = STAILQ_FIRST(&sg->hashes);
7061c6aec20Schristos for (i = 0; i < SIGN_HASH_DIVISION_NUM; i++) {
7071c6aec20Schristos old_qentry = qentry;
7081c6aec20Schristos qentry = STAILQ_NEXT(old_qentry, entries);
7091c6aec20Schristos STAILQ_REMOVE(&sg->hashes, old_qentry,
7101c6aec20Schristos string_queue, entries);
7111c6aec20Schristos FREEPTR(old_qentry->data);
7121c6aec20Schristos FREEPTR(old_qentry);
7131c6aec20Schristos }
7141c6aec20Schristos }
7151c6aec20Schristos return hashes_sent;
7161c6aec20Schristos }
7171c6aec20Schristos
7181c6aec20Schristos void
sign_free_hashes(struct signature_group_t * sg)7191c6aec20Schristos sign_free_hashes(struct signature_group_t *sg)
7201c6aec20Schristos {
7211c6aec20Schristos DPRINTF((D_CALL|D_SIGN), "sign_free_hashes(%p)\n", sg);
7221c6aec20Schristos sign_free_string_queue(&sg->hashes);
7231c6aec20Schristos }
7241c6aec20Schristos
7251c6aec20Schristos void
sign_free_string_queue(struct string_queue_head * sqhead)7261c6aec20Schristos sign_free_string_queue(struct string_queue_head *sqhead)
7271c6aec20Schristos {
7281c6aec20Schristos struct string_queue *qentry, *tmp_qentry;
7291c6aec20Schristos
7301c6aec20Schristos DPRINTF((D_CALL|D_SIGN), "sign_free_string_queue(%p)\n", sqhead);
7311c6aec20Schristos STAILQ_FOREACH_SAFE(qentry, sqhead, entries, tmp_qentry) {
7321c6aec20Schristos STAILQ_REMOVE(sqhead, qentry, string_queue, entries);
7331c6aec20Schristos FREEPTR(qentry->data);
7341c6aec20Schristos free(qentry);
7351c6aec20Schristos }
7361c6aec20Schristos assert(STAILQ_EMPTY(sqhead));
7371c6aec20Schristos }
7381c6aec20Schristos
7391c6aec20Schristos /*
7401c6aec20Schristos * hash one syslog message
7411c6aec20Schristos */
7421c6aec20Schristos bool
sign_msg_hash(char * line,char ** hash)7431c6aec20Schristos sign_msg_hash(char *line, char **hash)
7441c6aec20Schristos {
7451c6aec20Schristos unsigned char md_value[EVP_MAX_MD_SIZE];
7461c6aec20Schristos unsigned char md_b64[EVP_MAX_MD_SIZE*2];
7471c6aec20Schristos /* TODO: exact expression for b64 length? */
7481c6aec20Schristos unsigned md_len = 0;
7491c6aec20Schristos
7501c6aec20Schristos DPRINTF((D_CALL|D_SIGN), "sign_msg_hash('%s')\n", line);
7511c6aec20Schristos
7521c6aec20Schristos SSL_CHECK_ONE(EVP_DigestInit_ex(GlobalSign.mdctx, GlobalSign.md, NULL));
7531c6aec20Schristos SSL_CHECK_ONE(EVP_DigestUpdate(GlobalSign.mdctx, line, strlen(line)));
7541c6aec20Schristos SSL_CHECK_ONE(EVP_DigestFinal_ex(GlobalSign.mdctx, md_value, &md_len));
7551c6aec20Schristos
7561c6aec20Schristos b64_ntop(md_value, md_len, (char *)md_b64, EVP_MAX_MD_SIZE*2);
7571c6aec20Schristos *hash = strdup((char *)md_b64);
7581c6aec20Schristos
7591c6aec20Schristos DPRINTF((D_CALL|D_SIGN), "sign_msg_hash() --> \"%s\"\n", *hash);
7601c6aec20Schristos return true;
7611c6aec20Schristos }
7621c6aec20Schristos
7631c6aec20Schristos /*
7641c6aec20Schristos * append hash to SG queue
7651c6aec20Schristos */
7661c6aec20Schristos bool
sign_append_hash(char * hash,struct signature_group_t * sg)7671c6aec20Schristos sign_append_hash(char *hash, struct signature_group_t *sg)
7681c6aec20Schristos {
7691c6aec20Schristos struct string_queue *qentry;
7701c6aec20Schristos
7711c6aec20Schristos /* if one SG is shared by several destinations
7721c6aec20Schristos * prevent duplicate entries */
7731c6aec20Schristos if ((qentry = STAILQ_LAST(&sg->hashes, string_queue, entries))
7741c6aec20Schristos && !strcmp(qentry->data, hash)) {
7751c6aec20Schristos DPRINTF((D_CALL|D_SIGN), "sign_append_hash('%s', %p): "
7761c6aec20Schristos "hash already in queue\n", hash, sg);
7771c6aec20Schristos return false;
7781c6aec20Schristos }
7791c6aec20Schristos
7801c6aec20Schristos MALLOC(qentry, sizeof(*qentry));
7811c6aec20Schristos qentry->key = sign_assign_msg_num(sg);
7821c6aec20Schristos qentry->data = hash;
7831c6aec20Schristos STAILQ_INSERT_TAIL(&sg->hashes, qentry, entries);
7841c6aec20Schristos DPRINTF((D_CALL|D_SIGN), "sign_append_hash('%s', %p): "
7851c6aec20Schristos "#%" PRIdFAST64 "\n", hash, sg, qentry->key);
7861c6aec20Schristos return true;
7871c6aec20Schristos }
7881c6aec20Schristos
7891c6aec20Schristos /*
7901c6aec20Schristos * sign one syslog-sign message
7911c6aec20Schristos *
7921c6aec20Schristos * requires a ssign or ssigt-cert SD element
7931c6aec20Schristos * ending with ' SIGN=""]' in sd
7941c6aec20Schristos * linesize is available memory (= sizeof(sd))
7951c6aec20Schristos *
7961c6aec20Schristos * function will calculate signature and return a new buffer
7971c6aec20Schristos */
7981c6aec20Schristos bool
sign_msg_sign(struct buf_msg ** bufferptr,char * sd,size_t linesize)7991c6aec20Schristos sign_msg_sign(struct buf_msg **bufferptr, char *sd, size_t linesize)
8001c6aec20Schristos {
8011c6aec20Schristos char *signature, *line;
8021c6aec20Schristos size_t linelen, tlsprefixlen, endptr, newlinelen;
8031c6aec20Schristos struct buf_msg *buffer;
8041c6aec20Schristos
8051c6aec20Schristos DPRINTF((D_CALL|D_SIGN), "sign_msg_sign()\n");
8061c6aec20Schristos endptr = strlen(sd);
8071c6aec20Schristos
8081c6aec20Schristos assert(endptr < linesize);
8091c6aec20Schristos assert(sd[endptr] == '\0');
8101c6aec20Schristos assert(sd[endptr-1] == ']');
8111c6aec20Schristos assert(sd[endptr-2] == '"');
8121c6aec20Schristos
8131c6aec20Schristos /* set up buffer */
8141c6aec20Schristos buffer = buf_msg_new(0);
8157779d774Schristos buffer->timestamp = make_timestamp(NULL, !BSDOutputFormat, 0);
8161c6aec20Schristos buffer->prog = appname;
8171c6aec20Schristos buffer->pid = include_pid;
8181c6aec20Schristos buffer->recvhost = buffer->host = LocalFQDN;
8191c6aec20Schristos buffer->pri = 110;
8201c6aec20Schristos buffer->flags = IGN_CONS|SIGN_MSG;
8211c6aec20Schristos buffer->sd = sd;
8221c6aec20Schristos
8231c6aec20Schristos /* SD ready, now format and sign */
8241c6aec20Schristos if (!format_buffer(buffer, &line, &linelen, NULL,
8251c6aec20Schristos &tlsprefixlen, NULL)) {
8261c6aec20Schristos DPRINTF((D_CALL|D_SIGN), "sign_send_signature_block():"
8271c6aec20Schristos " format_buffer() failed\n");
8281c6aec20Schristos buffer->sd = NULL;
8291c6aec20Schristos DELREF(buffer);
8301c6aec20Schristos return false;
8311c6aec20Schristos }
8321c6aec20Schristos if (!sign_string_sign(line+tlsprefixlen, &signature)) {
8331c6aec20Schristos DPRINTF((D_CALL|D_SIGN), "sign_send_signature_block():"
8341c6aec20Schristos " sign_string_sign() failed\n");
8351c6aec20Schristos buffer->sd = NULL;
8361c6aec20Schristos DELREF(buffer);
8371c6aec20Schristos FREEPTR(line);
8381c6aec20Schristos return false;
8391c6aec20Schristos }
8401c6aec20Schristos FREEPTR(line);
8411c6aec20Schristos sd[endptr-2] = '\0';
8421c6aec20Schristos newlinelen = strlcat(sd, signature, linesize);
8431c6aec20Schristos newlinelen = strlcat(sd, "\"]", linesize);
8441c6aec20Schristos
8451c6aec20Schristos if (newlinelen >= linesize) {
8461c6aec20Schristos DPRINTF(D_SIGN, "sign_send_signature_block(): "
8471c6aec20Schristos "buffer too small\n");
8481c6aec20Schristos buffer->sd = NULL;
8491c6aec20Schristos DELREF(buffer);
8501c6aec20Schristos return false;
8511c6aec20Schristos }
8521c6aec20Schristos assert(newlinelen < linesize);
8531c6aec20Schristos assert(sd[newlinelen] == '\0');
8541c6aec20Schristos assert(sd[newlinelen-1] == ']');
8551c6aec20Schristos assert(sd[newlinelen-2] == '"');
8561c6aec20Schristos
8571c6aec20Schristos buffer->sd = strdup(sd);
8581c6aec20Schristos *bufferptr = buffer;
8591c6aec20Schristos return true;
8601c6aec20Schristos }
8611c6aec20Schristos
8621c6aec20Schristos /*
8631c6aec20Schristos * sign one string
8641c6aec20Schristos */
8651c6aec20Schristos bool
sign_string_sign(char * line,char ** signature)8661c6aec20Schristos sign_string_sign(char *line, char **signature)
8671c6aec20Schristos {
8681c6aec20Schristos char buf[SIGN_MAX_LENGTH+1];
8691c6aec20Schristos unsigned char sig_value[SIGN_B64SIGLEN_DSS];
8701c6aec20Schristos unsigned char sig_b64[SIGN_B64SIGLEN_DSS];
8711c6aec20Schristos unsigned sig_len = 0;
8721c6aec20Schristos char *p, *q;
8731c6aec20Schristos /*
8741c6aec20Schristos * The signature is calculated over the completely formatted
8751c6aec20Schristos * syslog-message, including all of the PRI, HEADER, and hashes
8761c6aec20Schristos * in the hash block, excluding spaces between fields, and also
8771c6aec20Schristos * excluding the signature field (SD Parameter Name "SIGN", "=",
8781c6aec20Schristos * and corresponding value).
8791c6aec20Schristos *
8801c6aec20Schristos * -- I am not quite sure which spaces are to be removed.
8811c6aec20Schristos * Only the ones inside the "ssign" element or those between
8821c6aec20Schristos * header fields as well?
8831c6aec20Schristos */
8841c6aec20Schristos /* removes the string ' SIGN=""' */
8851c6aec20Schristos for (p = line, q = buf;
8861c6aec20Schristos *p && (q - buf <= SIGN_MAX_LENGTH);) {
8871c6aec20Schristos if (strncmp(p, " SIGN=\"\"", 8) == 0)
8881c6aec20Schristos p += 8;
8891c6aec20Schristos *q++ = *p++;
8901c6aec20Schristos }
8911c6aec20Schristos *q = '\0';
8921c6aec20Schristos
8931c6aec20Schristos SSL_CHECK_ONE(EVP_SignInit(GlobalSign.sigctx, GlobalSign.sig));
8941c6aec20Schristos SSL_CHECK_ONE(EVP_SignUpdate(GlobalSign.sigctx, buf, q-buf));
8951c6aec20Schristos assert(GlobalSign.privkey);
8961c6aec20Schristos SSL_CHECK_ONE(EVP_SignFinal(GlobalSign.sigctx, sig_value, &sig_len,
8971c6aec20Schristos GlobalSign.privkey));
8981c6aec20Schristos
8991c6aec20Schristos b64_ntop(sig_value, sig_len, (char *)sig_b64, sizeof(sig_b64));
9001c6aec20Schristos *signature = strdup((char *)sig_b64);
9011c6aec20Schristos
9021c6aec20Schristos DPRINTF((D_CALL|D_SIGN), "sign_string_sign('%s') --> '%s'\n",
9031c6aec20Schristos buf, *signature);
9041c6aec20Schristos return *signature != NULL;
9051c6aec20Schristos }
9061c6aec20Schristos
9071c6aec20Schristos void
sign_new_reboot_session(void)90892dd0698Schristos sign_new_reboot_session(void)
9091c6aec20Schristos {
9101c6aec20Schristos struct signature_group_t *sg;
9111c6aec20Schristos
9121c6aec20Schristos DPRINTF((D_CALL|D_SIGN), "sign_new_reboot_session()\n");
9131c6aec20Schristos
9141c6aec20Schristos /* global counters */
9151c6aec20Schristos GlobalSign.gbc = 0;
9161c6aec20Schristos /* might be useful for later analysis:
9171c6aec20Schristos * rebooted session IDs are sequential,
9181c6aec20Schristos * normal IDs are almost always not */
9191c6aec20Schristos GlobalSign.rsid++;
9201c6aec20Schristos
9211c6aec20Schristos assert(GlobalSign.sg <= 3);
9221c6aec20Schristos /* reset SGs */
9231c6aec20Schristos STAILQ_FOREACH(sg, &GlobalSign.SigGroups, entries) {
9241c6aec20Schristos sg->resendcount = SIGN_RESENDCOUNT_CERTBLOCK;
9251c6aec20Schristos sg->last_msg_num = 1;
9261c6aec20Schristos }
9271c6aec20Schristos }
9281c6aec20Schristos
9291c6aec20Schristos /* get msg_num, increment counter, check overflow */
9301c6aec20Schristos uint_fast64_t
sign_assign_msg_num(struct signature_group_t * sg)9311c6aec20Schristos sign_assign_msg_num(struct signature_group_t *sg)
9321c6aec20Schristos {
9331c6aec20Schristos uint_fast64_t old;
9341c6aec20Schristos
9351c6aec20Schristos old = sg->last_msg_num++;
9361c6aec20Schristos if (sg->last_msg_num > SIGN_MAX_COUNT)
9371c6aec20Schristos sign_new_reboot_session();
9381c6aec20Schristos return old;
9391c6aec20Schristos }
9401c6aec20Schristos
9411c6aec20Schristos
9421c6aec20Schristos /* increment gbc, check overflow */
9431c6aec20Schristos void
sign_inc_gbc(void)94492dd0698Schristos sign_inc_gbc(void)
9451c6aec20Schristos {
9461c6aec20Schristos if (++GlobalSign.gbc > SIGN_MAX_COUNT)
9471c6aec20Schristos sign_new_reboot_session();
9481c6aec20Schristos }
9491c6aec20Schristos #endif /* !DISABLE_SIGN */
950