Home | History | Annotate | Line # | Download | only in syslogd
sign.c revision 1.3.8.2
      1  1.3.8.2      yamt /*	$NetBSD: sign.c,v 1.3.8.2 2012/10/30 19:00:52 yamt Exp $	*/
      2      1.1  christos 
      3      1.1  christos /*-
      4      1.1  christos  * Copyright (c) 2008 The NetBSD Foundation, Inc.
      5      1.1  christos  * All rights reserved.
      6      1.1  christos  *
      7      1.1  christos  * This code is derived from software contributed to The NetBSD Foundation
      8      1.1  christos  * by Martin Schtte.
      9      1.1  christos  *
     10      1.1  christos  * Redistribution and use in source and binary forms, with or without
     11      1.1  christos  * modification, are permitted provided that the following conditions
     12      1.1  christos  * are met:
     13      1.1  christos  * 1. Redistributions of source code must retain the above copyright
     14      1.1  christos  *    notice, this list of conditions and the following disclaimer.
     15      1.1  christos  * 2. Redistributions in binary form must reproduce the above copyright
     16      1.1  christos  *    notice, this list of conditions and the following disclaimer in the
     17      1.1  christos  *    documentation and/or other materials provided with the distribution.
     18      1.1  christos  * 3. All advertising materials mentioning features or use of this software
     19      1.1  christos  *    must display the following acknowledgement:
     20      1.1  christos  *        This product includes software developed by the NetBSD
     21      1.1  christos  *        Foundation, Inc. and its contributors.
     22      1.1  christos  * 4. Neither the name of The NetBSD Foundation nor the names of its
     23      1.1  christos  *    contributors may be used to endorse or promote products derived
     24      1.1  christos  *    from this software without specific prior written permission.
     25      1.1  christos  *
     26      1.1  christos  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     27      1.1  christos  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     28      1.1  christos  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     29      1.1  christos  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     30      1.1  christos  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     31      1.1  christos  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     32      1.1  christos  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     33      1.1  christos  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     34      1.1  christos  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     35      1.1  christos  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     36      1.1  christos  * POSSIBILITY OF SUCH DAMAGE.
     37      1.1  christos  */
     38      1.1  christos /*
     39      1.1  christos  * sign.c
     40      1.1  christos  * syslog-sign related code for syslogd
     41      1.1  christos  *
     42      1.1  christos  * Martin Schtte
     43      1.1  christos  */
     44      1.2   minskim /*
     45      1.2   minskim  * Issues with the current internet draft:
     46      1.1  christos  * 1. The draft is a bit unclear on the input format for the signature,
     47      1.1  christos  *    so this might have to be changed later. Cf. sign_string_sign()
     48      1.1  christos  * 2. The draft only defines DSA signatures. I hope it will be extended
     49      1.1  christos  *    to DSS, thus allowing DSA, RSA (ANSI X9.31) and ECDSA (ANSI X9.62)
     50      1.1  christos  * 3. The draft does not define the data format for public keys in CBs.
     51      1.1  christos  *    This implementation sends public keys in DER encoding.
     52      1.1  christos  * 4. This current implementation uses high-level OpenSSL API.
     53      1.1  christos  *    I am not sure if these completely implement the FIPS/ANSI standards.
     54      1.1  christos  * Update after WG discussion in August:
     55      1.1  christos  * 1. check; next draft will be clearer and specify the format as implemented.
     56      1.1  christos  * 2. check; definitely only DSA in this version.
     57      1.1  christos  * 3. remains a problem, so far no statement from authors or WG.
     58      1.2   minskim  * 4. check; used EVP_dss1 method implements FIPS.
     59      1.1  christos  */
     60      1.2   minskim /*
     61      1.1  christos  * Limitations of this implementation:
     62      1.1  christos  * - cannot use OpenPGP keys, only PKIX or DSA due to OpenSSL capabilities
     63      1.1  christos  * - only works for correctly formatted messages, because incorrect messages
     64      1.1  christos  *   are reformatted (e.g. if it receives a message with two spaces between
     65      1.1  christos  *   fields it might even be parsed, but the output will have only one space).
     66      1.1  christos  */
     67      1.1  christos 
     68      1.1  christos #include <sys/cdefs.h>
     69  1.3.8.2      yamt __RCSID("$NetBSD: sign.c,v 1.3.8.2 2012/10/30 19:00:52 yamt Exp $");
     70      1.2   minskim 
     71      1.1  christos #ifndef DISABLE_SIGN
     72      1.1  christos #include "syslogd.h"
     73      1.1  christos #ifndef DISABLE_TLS
     74      1.1  christos #include "tls.h"
     75      1.1  christos #endif /* !DISABLE_TLS */
     76      1.1  christos #include "sign.h"
     77      1.1  christos #include "extern.h"
     78      1.1  christos 
     79      1.1  christos /*
     80      1.2   minskim  * init all SGs for a given algorithm
     81      1.1  christos  */
     82      1.1  christos bool
     83      1.1  christos sign_global_init(struct filed *Files)
     84      1.1  christos {
     85      1.1  christos 	DPRINTF((D_CALL|D_SIGN), "sign_global_init()\n");
     86      1.1  christos 	if (!(GlobalSign.sg == 0 || GlobalSign.sg == 1
     87      1.1  christos 	   || GlobalSign.sg == 2 || GlobalSign.sg == 3)) {
     88      1.1  christos 		logerror("sign_init(): invalid SG %d", GlobalSign.sg);
     89      1.1  christos 		return false;
     90      1.1  christos 	}
     91      1.1  christos 
     92      1.1  christos 	if (!sign_get_keys())
     93      1.1  christos 		return false;
     94      1.1  christos 
     95      1.1  christos 	/* signature algorithm */
     96      1.1  christos 	/* can probably be merged with the hash algorithm/context but
     97      1.1  christos 	 * I leave the optimization for later until the RFC is ready */
     98      1.1  christos 	GlobalSign.sigctx = EVP_MD_CTX_create();
     99      1.1  christos 	EVP_MD_CTX_init(GlobalSign.sigctx);
    100      1.1  christos 
    101      1.1  christos 	/* the signature algorithm depends on the type of key */
    102      1.1  christos 	if (EVP_PKEY_DSA == EVP_PKEY_type(GlobalSign.pubkey->type)) {
    103      1.1  christos 		GlobalSign.sig = EVP_dss1();
    104      1.1  christos 		GlobalSign.sig_len_b64 = SIGN_B64SIGLEN_DSS;
    105      1.1  christos /* this is the place to add non-DSA key types and algorithms
    106      1.1  christos 	} else if (EVP_PKEY_RSA == EVP_PKEY_type(GlobalSign.pubkey->type)) {
    107      1.1  christos 		GlobalSign.sig = EVP_sha1();
    108      1.1  christos 		GlobalSign.sig_len_b64 = 28;
    109      1.1  christos */
    110      1.1  christos 	} else {
    111      1.1  christos 		logerror("key type not supported for syslog-sign");
    112      1.1  christos 		return false;
    113      1.1  christos 	}
    114      1.1  christos 
    115      1.1  christos 	assert(GlobalSign.keytype == 'C' || GlobalSign.keytype == 'K');
    116      1.1  christos 	assert(GlobalSign.pubkey_b64 && GlobalSign.privkey &&
    117      1.1  christos 	    GlobalSign.pubkey);
    118      1.1  christos 	assert(GlobalSign.privkey->pkey.dsa->priv_key);
    119      1.2   minskim 
    120      1.1  christos 	GlobalSign.gbc = 0;
    121      1.1  christos 	STAILQ_INIT(&GlobalSign.SigGroups);
    122      1.1  christos 
    123      1.1  christos 	/* hash algorithm */
    124      1.1  christos 	OpenSSL_add_all_digests();
    125      1.1  christos 	GlobalSign.mdctx = EVP_MD_CTX_create();
    126      1.1  christos 	EVP_MD_CTX_init(GlobalSign.mdctx);
    127      1.1  christos 
    128      1.1  christos 	/* values for SHA-1 */
    129      1.1  christos 	GlobalSign.md = EVP_dss1();
    130      1.1  christos 	GlobalSign.md_len_b64 = 28;
    131      1.1  christos 	GlobalSign.ver = "0111";
    132      1.1  christos 
    133      1.1  christos 	if (!sign_sg_init(Files))
    134      1.1  christos 		return false;
    135      1.1  christos 	sign_new_reboot_session();
    136      1.2   minskim 
    137      1.1  christos 	DPRINTF(D_SIGN, "length values: SIGN_MAX_SD_LENGTH %d, "
    138      1.1  christos 	    "SIGN_MAX_FRAG_LENGTH %d, SIGN_MAX_SB_LENGTH %d, "
    139      1.1  christos 	    "SIGN_MAX_HASH_NUM %d\n", SIGN_MAX_SD_LENGTH,
    140      1.1  christos 	    SIGN_MAX_FRAG_LENGTH, SIGN_MAX_SB_LENGTH, SIGN_MAX_HASH_NUM);
    141      1.1  christos 
    142      1.1  christos 	/* set just before return, so it indicates initialization */
    143      1.1  christos 	GlobalSign.rsid = now;
    144      1.1  christos 	return true;
    145      1.1  christos }
    146      1.1  christos 
    147      1.1  christos /*
    148      1.1  christos  * get keys for syslog-sign
    149      1.1  christos  * either from the X.509 certificate used for TLS
    150      1.1  christos  * or by generating a new one
    151      1.2   minskim  *
    152      1.1  christos  * sets the global variables
    153      1.1  christos  * GlobalSign.keytype, GlobalSign.pubkey_b64,
    154      1.1  christos  * GlobalSign.privkey, and GlobalSign.pubkey
    155      1.1  christos  */
    156      1.1  christos bool
    157  1.3.8.2      yamt sign_get_keys(void)
    158      1.1  christos {
    159      1.1  christos 	EVP_PKEY *pubkey = NULL, *privkey = NULL;
    160      1.1  christos 	unsigned char *der_pubkey = NULL, *ptr_der_pubkey = NULL;
    161      1.1  christos 	char *pubkey_b64 = NULL;
    162      1.1  christos 	int der_len;
    163      1.2   minskim 
    164      1.1  christos 	/* try PKIX/TLS key first */
    165      1.1  christos #ifndef DISABLE_TLS
    166      1.1  christos 	SSL *ssl;
    167      1.1  christos 	if (tls_opt.global_TLS_CTX
    168      1.1  christos 	 && (ssl = SSL_new(tls_opt.global_TLS_CTX))) {
    169      1.1  christos 		X509 *cert;
    170      1.1  christos 		DPRINTF(D_SIGN, "Try to get keys from TLS X.509 cert...\n");
    171      1.2   minskim 
    172      1.1  christos 		if (!(cert = SSL_get_certificate(ssl))) {
    173      1.1  christos 			logerror("SSL_get_certificate() failed");
    174      1.1  christos 			FREE_SSL(ssl);
    175      1.1  christos 			return false;
    176      1.1  christos 		}
    177      1.1  christos 		if (!(privkey = SSL_get_privatekey(ssl))) {
    178      1.1  christos 			logerror("SSL_get_privatekey() failed");
    179      1.1  christos 			FREE_SSL(ssl);
    180      1.1  christos 			return false;
    181      1.1  christos 		}
    182      1.1  christos 		if (!(pubkey = X509_get_pubkey(cert))) {
    183      1.1  christos 			logerror("X509_get_pubkey() failed");
    184      1.1  christos 			FREE_SSL(ssl);
    185      1.1  christos 			return false;
    186      1.1  christos 		}
    187      1.1  christos 		/* note:
    188      1.1  christos 		 * - privkey is just a pointer into SSL_CTX and
    189      1.1  christos 		 *   must not be changed nor be free()d
    190      1.1  christos 		 * - but pubkey has to be freed with EVP_PKEY_free()
    191      1.1  christos 		 */
    192      1.1  christos 		FREE_SSL(ssl);
    193      1.1  christos 
    194      1.1  christos 		if (EVP_PKEY_DSA != EVP_PKEY_type(pubkey->type)) {
    195      1.1  christos 			DPRINTF(D_SIGN, "X.509 cert has no DSA key\n");
    196      1.1  christos 			EVP_PKEY_free(pubkey);
    197      1.1  christos 			privkey = NULL;
    198      1.1  christos 			pubkey = NULL;
    199      1.1  christos 		} else {
    200      1.1  christos 			DPRINTF(D_SIGN, "Got public and private key "
    201      1.1  christos 			    "from X.509 --> use type PKIX\n");
    202      1.1  christos 			GlobalSign.keytype = 'C';
    203      1.1  christos 			GlobalSign.privkey = privkey;
    204      1.1  christos 			GlobalSign.pubkey = pubkey;
    205      1.2   minskim 
    206      1.1  christos 			/* base64 certificate encoding */
    207      1.1  christos 			der_len = i2d_X509(cert, NULL);
    208      1.1  christos 			if (!(ptr_der_pubkey = der_pubkey = malloc(der_len))
    209      1.1  christos 			    || !(pubkey_b64 = malloc(der_len*2))) {
    210      1.1  christos 				free(der_pubkey);
    211      1.1  christos 				logerror("malloc() failed");
    212      1.1  christos 				return false;
    213      1.1  christos 			}
    214      1.1  christos 			if (i2d_X509(cert, &ptr_der_pubkey) <= 0) {
    215      1.1  christos 				logerror("i2d_X509() failed");
    216      1.1  christos 				return false;
    217      1.1  christos 			}
    218      1.1  christos 			b64_ntop(der_pubkey, der_len, pubkey_b64, der_len*2);
    219      1.1  christos 			free(der_pubkey);
    220      1.1  christos 			/* try to resize memory object as needed */
    221      1.1  christos 			GlobalSign.pubkey_b64 = realloc(pubkey_b64,
    222      1.1  christos 							strlen(pubkey_b64)+1);
    223      1.1  christos 			if (!GlobalSign.pubkey_b64)
    224      1.1  christos 				GlobalSign.pubkey_b64 = pubkey_b64;
    225      1.1  christos 		}
    226      1.1  christos 	}
    227      1.1  christos #endif /* !DISABLE_TLS */
    228      1.1  christos 	if (!(privkey && pubkey)) { /* PKIX not available --> generate key */
    229      1.1  christos 		DSA *dsa;
    230      1.1  christos 
    231      1.1  christos 		DPRINTF(D_SIGN, "Unable to get keys from X.509 "
    232      1.1  christos 			"--> use DSA with type 'K'\n");
    233      1.1  christos 		if (!(privkey = EVP_PKEY_new())) {
    234      1.1  christos 			logerror("EVP_PKEY_new() failed");
    235      1.1  christos 			return false;
    236      1.1  christos 		}
    237      1.1  christos 		dsa = DSA_generate_parameters(SIGN_GENCERT_BITS, NULL, 0,
    238      1.1  christos 			NULL, NULL, NULL, NULL);
    239      1.1  christos 		if (!DSA_generate_key(dsa)) {
    240      1.1  christos 			logerror("DSA_generate_key() failed");
    241      1.1  christos 			return false;
    242      1.1  christos 		}
    243      1.1  christos 		if (!EVP_PKEY_assign_DSA(privkey, dsa)) {
    244      1.1  christos 			logerror("EVP_PKEY_assign_DSA() failed");
    245      1.1  christos 			return false;
    246      1.1  christos 		}
    247      1.1  christos 		GlobalSign.keytype = 'K';  /* public/private keys used */
    248      1.1  christos 		GlobalSign.privkey = privkey;
    249      1.1  christos 		GlobalSign.pubkey = privkey;
    250      1.1  christos 
    251      1.1  christos 		/* pubkey base64 encoding */
    252      1.1  christos 		der_len = i2d_DSA_PUBKEY(dsa, NULL);
    253      1.1  christos 		if (!(ptr_der_pubkey = der_pubkey = malloc(der_len))
    254      1.1  christos 		 || !(pubkey_b64 = malloc(der_len*2))) {
    255      1.1  christos 			free(der_pubkey);
    256      1.1  christos 			logerror("malloc() failed");
    257      1.1  christos 			return false;
    258      1.1  christos 		}
    259      1.1  christos 		if (i2d_DSA_PUBKEY(dsa, &ptr_der_pubkey) <= 0) {
    260      1.1  christos 			logerror("i2d_DSA_PUBKEY() failed");
    261  1.3.8.1      yamt 			free(der_pubkey);
    262  1.3.8.1      yamt 			free(pubkey_b64);
    263      1.1  christos 			return false;
    264      1.1  christos 		}
    265      1.1  christos 		b64_ntop(der_pubkey, der_len, pubkey_b64, der_len*2);
    266      1.1  christos 		free(der_pubkey);
    267      1.1  christos 		/* try to resize memory object as needed */
    268      1.1  christos 		GlobalSign.pubkey_b64 = realloc(pubkey_b64,
    269      1.1  christos 		    strlen(pubkey_b64) + 1);
    270      1.1  christos 		if (!GlobalSign.pubkey_b64)
    271      1.1  christos 			GlobalSign.pubkey_b64 = pubkey_b64;
    272      1.1  christos 	}
    273      1.1  christos 	return true;
    274      1.1  christos }
    275      1.1  christos 
    276      1.1  christos /*
    277      1.2   minskim  * init SGs
    278      1.1  christos  */
    279      1.1  christos bool
    280      1.1  christos sign_sg_init(struct filed *Files)
    281      1.1  christos {
    282      1.1  christos 	struct signature_group_t *sg, *newsg, *last_sg;
    283      1.1  christos 	struct filed_queue	 *fq;
    284      1.1  christos 	struct string_queue	 *sqentry, *last_sqentry;
    285      1.1  christos 	struct filed *f;
    286      1.3     lukem 	unsigned int i;
    287      1.1  christos 
    288      1.1  christos 	/* note on SG 1 and 2:
    289      1.1  christos 	 * it is assumed that redundant signature groups
    290      1.1  christos 	 * and especially signature groups without an associated
    291      1.1  christos 	 * destination are harmless.
    292      1.1  christos 	 * this currently holds true because sign_append_hash()
    293      1.1  christos 	 * is called from fprintlog(), so only actually used
    294      1.1  christos 	 * signature group get hashes and need memory for them
    295      1.1  christos 	 */
    296      1.1  christos 	/* possible optimization for SGs 1 and 2:
    297      1.1  christos 	 * use a struct signature_group_t *newsg[IETF_NUM_PRIVALUES]
    298      1.1  christos 	 * for direct group lookup
    299      1.1  christos 	 */
    300      1.1  christos 
    301      1.1  christos #define ALLOC_OR_FALSE(x) do {				\
    302      1.1  christos 	if(!((x) = calloc(1, sizeof(*(x))))) {		\
    303      1.1  christos 		logerror("Unable to allocate memory");	\
    304      1.1  christos 		return false;				\
    305      1.1  christos 	}						\
    306      1.1  christos } while (/*CONSTCOND*/0)
    307      1.1  christos 
    308      1.1  christos #define ALLOC_SG(x) do {				\
    309      1.1  christos 	ALLOC_OR_FALSE(x);				\
    310      1.1  christos 	(x)->last_msg_num = 1; /* cf. section 4.2.5 */	\
    311      1.1  christos 	STAILQ_INIT(&(x)->hashes);			\
    312      1.1  christos 	STAILQ_INIT(&(x)->files);			\
    313      1.1  christos } while (/*CONSTCOND*/0)
    314      1.1  christos 
    315      1.1  christos /* alloc(fq) and add to SGs file queue */
    316      1.1  christos #define ASSIGN_FQ() do {				\
    317      1.1  christos 	ALLOC_OR_FALSE(fq);				\
    318      1.1  christos 	fq->f = f;					\
    319      1.1  christos 	f->f_sg = newsg;				\
    320      1.1  christos 	DPRINTF(D_SIGN, "SG@%p <--> f@%p\n", newsg, f); \
    321      1.1  christos 	STAILQ_INSERT_TAIL(&newsg->files, fq, entries); \
    322      1.1  christos } while (/*CONSTCOND*/0)
    323      1.1  christos 
    324      1.1  christos 	switch (GlobalSign.sg) {
    325      1.1  christos 	case 0:
    326      1.1  christos 		/* one SG, linked to all files */
    327      1.1  christos 		ALLOC_SG(newsg);
    328      1.1  christos 		newsg->spri = 0;
    329      1.1  christos 		for (f = Files; f; f = f->f_next)
    330      1.1  christos 			ASSIGN_FQ();
    331      1.1  christos 		STAILQ_INSERT_TAIL(&GlobalSign.SigGroups,
    332      1.1  christos 			newsg, entries);
    333      1.1  christos 		break;
    334      1.1  christos 	case 1:
    335      1.1  christos 		/* every PRI gets one SG */
    336      1.1  christos 		for (i = 0; i < IETF_NUM_PRIVALUES; i++) {
    337      1.1  christos 			int fac, prilev;
    338      1.1  christos 			fac = LOG_FAC(i);
    339      1.1  christos 			prilev = LOG_PRI(i);
    340      1.1  christos 			ALLOC_SG(newsg);
    341      1.1  christos 			newsg->spri = i;
    342      1.1  christos 
    343      1.1  christos 			/* now find all destinations associated with this SG */
    344      1.1  christos 			for (f = Files; f; f = f->f_next)
    345      1.1  christos 				/* check priorities */
    346      1.1  christos 				if (MATCH_PRI(f, fac, prilev))
    347      1.1  christos 					ASSIGN_FQ();
    348      1.1  christos 			STAILQ_INSERT_TAIL(&GlobalSign.SigGroups,
    349      1.1  christos 				newsg, entries);
    350      1.1  christos 		}
    351      1.1  christos 		break;
    352      1.1  christos 	case 2:
    353      1.1  christos 		/* PRI ranges get one SG, boundaries given by the
    354      1.1  christos 		 * SPRI, indicating the largest PRI in the SG
    355      1.2   minskim 		 *
    356      1.1  christos 		 * either GlobalSign.sig2_delims has a list of
    357      1.1  christos 		 * user configured delimiters, or we use a default
    358      1.1  christos 		 * and set up one SG per facility
    359      1.1  christos 		 */
    360      1.1  christos 		if (STAILQ_EMPTY(&GlobalSign.sig2_delims)) {
    361      1.1  christos 			DPRINTF(D_SIGN, "sign_sg_init(): set default "
    362      1.1  christos 			    "values for SG 2\n");
    363      1.1  christos 			for (i = 0; i < (IETF_NUM_PRIVALUES>>3); i++) {
    364      1.1  christos 				ALLOC_OR_FALSE(sqentry);
    365      1.1  christos 				sqentry->data = NULL;
    366      1.1  christos 				sqentry->key = (i<<3);
    367      1.1  christos 				STAILQ_INSERT_TAIL(&GlobalSign.sig2_delims,
    368      1.1  christos 					sqentry, entries);
    369      1.1  christos 			}
    370      1.1  christos 		}
    371      1.1  christos 		assert(!STAILQ_EMPTY(&GlobalSign.sig2_delims));
    372      1.1  christos 
    373      1.1  christos 		/* add one more group at the end */
    374      1.1  christos 		last_sqentry = STAILQ_LAST(&GlobalSign.sig2_delims,
    375      1.1  christos 			string_queue, entries);
    376      1.1  christos 		if (last_sqentry->key < IETF_NUM_PRIVALUES) {
    377      1.1  christos 			ALLOC_OR_FALSE(sqentry);
    378      1.1  christos 			sqentry->data = NULL;
    379      1.1  christos 			sqentry->key = IETF_NUM_PRIVALUES-1;
    380      1.1  christos 			STAILQ_INSERT_TAIL(&GlobalSign.sig2_delims,
    381      1.1  christos 				sqentry, entries);
    382      1.1  christos 		}
    383      1.1  christos 
    384      1.1  christos 		STAILQ_FOREACH(sqentry, &GlobalSign.sig2_delims, entries) {
    385      1.3     lukem 			unsigned int min_pri = 0;
    386      1.1  christos 			ALLOC_SG(newsg);
    387      1.1  christos 			newsg->spri = sqentry->key;
    388      1.1  christos 
    389      1.1  christos 			/* check _all_ priorities in SG */
    390      1.1  christos 			last_sg = STAILQ_LAST(&GlobalSign.SigGroups,
    391      1.1  christos 			    signature_group_t, entries);
    392      1.1  christos 			if (last_sg)
    393      1.1  christos 				min_pri = last_sg->spri + 1;
    394      1.1  christos 
    395      1.1  christos 			DPRINTF(D_SIGN, "sign_sg_init(): add SG@%p: SG=\"2\","
    396      1.1  christos 			    " SPRI=\"%d\" -- for msgs with "
    397      1.1  christos 			    "%d <= pri <= %d\n",
    398      1.1  christos 			    newsg, newsg->spri, min_pri, newsg->spri);
    399      1.1  christos 			/* now find all destinations associated with this SG */
    400      1.1  christos 			for (f = Files; f; f = f->f_next) {
    401      1.1  christos 				bool match = false;
    402      1.1  christos 				for (i = min_pri; i <= newsg->spri; i++) {
    403      1.1  christos 					int fac, prilev;
    404      1.1  christos 					fac = LOG_FAC(i);
    405      1.1  christos 					prilev = LOG_PRI(i);
    406      1.1  christos 					if (MATCH_PRI(f, fac, prilev)) {
    407      1.1  christos 						match = true;
    408      1.1  christos 						break;
    409      1.1  christos 					}
    410      1.1  christos 				}
    411      1.1  christos 				if (match)
    412      1.1  christos 					ASSIGN_FQ();
    413      1.1  christos 			}
    414      1.1  christos 			STAILQ_INSERT_TAIL(&GlobalSign.SigGroups,
    415      1.1  christos 			    newsg, entries);
    416      1.1  christos 		}
    417      1.1  christos 		break;
    418      1.1  christos 	case 3:
    419      1.1  christos 		/* every file (with flag) gets one SG */
    420      1.1  christos 		for (f = Files; f; f = f->f_next) {
    421      1.1  christos 			if (!(f->f_flags & FFLAG_SIGN)) {
    422      1.1  christos 				f->f_sg = NULL;
    423      1.1  christos 				continue;
    424      1.1  christos 			}
    425      1.1  christos 			ALLOC_SG(newsg);
    426      1.1  christos 			newsg->spri = f->f_file; /* not needed but shows SGs */
    427      1.1  christos 			ASSIGN_FQ();
    428      1.1  christos 			STAILQ_INSERT_TAIL(&GlobalSign.SigGroups,
    429      1.1  christos 			    newsg, entries);
    430      1.1  christos 		}
    431      1.1  christos 		break;
    432      1.1  christos 	}
    433      1.1  christos 	DPRINTF((D_PARSE|D_SIGN), "sign_sg_init() set up these "
    434      1.1  christos 	    "Signature Groups:\n");
    435      1.1  christos 	STAILQ_FOREACH(sg, &GlobalSign.SigGroups, entries) {
    436      1.1  christos 		DPRINTF((D_PARSE|D_SIGN), "SG@%p with SG=\"%d\", SPRI=\"%d\","
    437      1.1  christos 		    " associated files:\n", sg, GlobalSign.sg, sg->spri);
    438      1.1  christos 		STAILQ_FOREACH(fq, &sg->files, entries) {
    439      1.1  christos 			DPRINTF((D_PARSE|D_SIGN), "    f@%p with type %d\n",
    440      1.1  christos 			    fq->f, fq->f->f_type);
    441      1.1  christos 		}
    442      1.1  christos 	}
    443      1.1  christos 	return true;
    444      1.1  christos }
    445      1.1  christos 
    446      1.1  christos /*
    447      1.2   minskim  * free all SGs for a given algorithm
    448      1.1  christos  */
    449      1.1  christos void
    450  1.3.8.2      yamt sign_global_free(void)
    451      1.1  christos {
    452      1.1  christos 	struct signature_group_t *sg, *tmp_sg;
    453      1.1  christos 	struct filed_queue *fq, *tmp_fq;
    454      1.1  christos 
    455      1.1  christos 	DPRINTF((D_CALL|D_SIGN), "sign_global_free()\n");
    456      1.1  christos 	STAILQ_FOREACH_SAFE(sg, &GlobalSign.SigGroups, entries, tmp_sg) {
    457      1.1  christos 		if (!STAILQ_EMPTY(&sg->hashes)) {
    458      1.1  christos 			/* send CB and SB twice to get minimal redundancy
    459      1.1  christos 			 * for the last few message hashes */
    460      1.1  christos 			sign_send_certificate_block(sg);
    461      1.1  christos 			sign_send_certificate_block(sg);
    462      1.1  christos 			sign_send_signature_block(sg, true);
    463      1.1  christos 			sign_send_signature_block(sg, true);
    464      1.1  christos 			sign_free_hashes(sg);
    465      1.1  christos 		}
    466      1.1  christos 		fq = STAILQ_FIRST(&sg->files);
    467      1.1  christos 		while (fq != NULL) {
    468      1.1  christos 			tmp_fq = STAILQ_NEXT(fq, entries);
    469      1.1  christos 			free(fq);
    470      1.1  christos 			fq = tmp_fq;
    471      1.1  christos 		}
    472      1.1  christos 		STAILQ_REMOVE(&GlobalSign.SigGroups,
    473      1.1  christos 			sg, signature_group_t, entries);
    474      1.1  christos 		free(sg);
    475      1.1  christos 	}
    476      1.1  christos 	sign_free_string_queue(&GlobalSign.sig2_delims);
    477      1.1  christos 
    478      1.1  christos 	if (GlobalSign.privkey) {
    479      1.1  christos 		GlobalSign.privkey = NULL;
    480      1.1  christos 	}
    481      1.1  christos 	if (GlobalSign.pubkey) {
    482      1.1  christos 		EVP_PKEY_free(GlobalSign.pubkey);
    483      1.1  christos 		GlobalSign.pubkey = NULL;
    484      1.2   minskim 	}
    485      1.1  christos 	if(GlobalSign.mdctx) {
    486      1.1  christos 		EVP_MD_CTX_destroy(GlobalSign.mdctx);
    487      1.1  christos 		GlobalSign.mdctx = NULL;
    488      1.1  christos 	}
    489      1.1  christos 	if(GlobalSign.sigctx) {
    490      1.1  christos 		EVP_MD_CTX_destroy(GlobalSign.sigctx);
    491      1.1  christos 		GlobalSign.sigctx = NULL;
    492      1.1  christos 	}
    493      1.1  christos 	FREEPTR(GlobalSign.pubkey_b64);
    494      1.1  christos }
    495      1.1  christos 
    496      1.1  christos /*
    497      1.1  christos  * create and send certificate block
    498      1.1  christos  */
    499      1.1  christos bool
    500      1.1  christos sign_send_certificate_block(struct signature_group_t *sg)
    501      1.1  christos {
    502      1.1  christos 	struct filed_queue *fq;
    503      1.1  christos 	struct buf_msg *buffer;
    504      1.1  christos 	char *tstamp;
    505      1.1  christos 	char payload[SIGN_MAX_PAYLOAD_LENGTH];
    506      1.1  christos 	char sd[SIGN_MAX_SD_LENGTH];
    507      1.1  christos 	size_t payload_len, sd_len, fragment_len;
    508      1.1  christos 	size_t payload_index = 0;
    509      1.1  christos 
    510      1.1  christos 	/* do nothing if CBs already sent or if there was no message in SG */
    511      1.1  christos 	if (!sg->resendcount
    512      1.1  christos 	    || ((sg->resendcount == SIGN_RESENDCOUNT_CERTBLOCK)
    513      1.1  christos 	    && STAILQ_EMPTY(&sg->hashes)))
    514      1.1  christos 		return false;
    515      1.1  christos 
    516      1.1  christos 	DPRINTF((D_CALL|D_SIGN), "sign_send_certificate_block(%p)\n", sg);
    517      1.1  christos 	tstamp = make_timestamp(NULL, true);
    518      1.1  christos 
    519      1.1  christos 	payload_len = snprintf(payload, sizeof(payload), "%s %c %s", tstamp,
    520      1.1  christos 		GlobalSign.keytype, GlobalSign.pubkey_b64);
    521      1.1  christos 	if (payload_len >= sizeof(payload)) {
    522      1.1  christos 		DPRINTF(D_SIGN, "Buffer too small for syslog-sign setup\n");
    523      1.1  christos 		return false;
    524      1.1  christos 	}
    525      1.1  christos 
    526      1.1  christos 	while (payload_index < payload_len) {
    527      1.1  christos 		if (payload_len - payload_index <= SIGN_MAX_FRAG_LENGTH)
    528      1.1  christos 			fragment_len = payload_len - payload_index;
    529      1.1  christos 		else
    530      1.1  christos 			fragment_len = SIGN_MAX_FRAG_LENGTH;
    531      1.1  christos 
    532      1.1  christos 		/* format SD */
    533      1.1  christos 		sd_len = snprintf(sd, sizeof(sd), "[ssign-cert "
    534      1.1  christos 		    "VER=\"%s\" RSID=\"%" PRIuFAST64 "\" SG=\"%d\" "
    535      1.1  christos 		    "SPRI=\"%d\" TBPL=\"%zu\" INDEX=\"%zu\" "
    536      1.1  christos 		    "FLEN=\"%zu\" FRAG=\"%.*s\" "
    537      1.1  christos 		    "SIGN=\"\"]",
    538      1.1  christos 		    GlobalSign.ver, GlobalSign.rsid, GlobalSign.sg,
    539      1.1  christos 		    sg->spri, payload_len, payload_index+1,
    540      1.1  christos 		    fragment_len, (int)fragment_len,
    541      1.1  christos 		    &payload[payload_index]);
    542      1.1  christos 		assert(sd_len < sizeof(sd));
    543      1.1  christos 		assert(sd[sd_len] == '\0');
    544      1.1  christos 		assert(sd[sd_len-1] == ']');
    545      1.1  christos 		assert(sd[sd_len-2] == '"');
    546      1.2   minskim 
    547      1.1  christos 		if (!sign_msg_sign(&buffer, sd, sizeof(sd)))
    548      1.1  christos 			return 0;
    549      1.1  christos 		DPRINTF((D_CALL|D_SIGN), "sign_send_certificate_block(): "
    550      1.1  christos 		    "calling fprintlog()\n");
    551      1.1  christos 
    552      1.1  christos 		STAILQ_FOREACH(fq, &sg->files, entries) {
    553      1.1  christos 			/* we have to preserve the f_prevcount */
    554      1.1  christos 			int tmpcnt;
    555      1.1  christos 			tmpcnt = fq->f->f_prevcount;
    556      1.1  christos 			fprintlog(fq->f, buffer, NULL);
    557      1.1  christos 			fq->f->f_prevcount = tmpcnt;
    558      1.1  christos 		}
    559      1.1  christos 		sign_inc_gbc();
    560      1.1  christos 		DELREF(buffer);
    561      1.1  christos 		payload_index += fragment_len;
    562      1.1  christos 	}
    563      1.1  christos 	sg->resendcount--;
    564      1.1  christos 	return true;
    565      1.1  christos }
    566      1.1  christos 
    567      1.1  christos /*
    568      1.1  christos  * determine the SG for a message
    569      1.1  christos  * returns NULL if -sign not configured or no SG for this priority
    570      1.1  christos  */
    571      1.1  christos struct signature_group_t *
    572      1.1  christos sign_get_sg(int pri, struct filed *f)
    573      1.1  christos {
    574      1.1  christos 	struct signature_group_t *sg, *rc = NULL;
    575      1.2   minskim 
    576      1.1  christos 	if (GlobalSign.rsid && f)
    577      1.1  christos 		switch (GlobalSign.sg) {
    578      1.1  christos 		case 0:
    579      1.1  christos 			rc = f->f_sg;
    580      1.1  christos 			break;
    581      1.1  christos 		case 1:
    582      1.1  christos 		case 2:
    583      1.1  christos 			STAILQ_FOREACH(sg, &GlobalSign.SigGroups, entries) {
    584      1.3     lukem 				if (sg->spri >= (unsigned int)pri) {
    585      1.1  christos 					rc = sg;
    586      1.1  christos 					break;
    587      1.1  christos 				}
    588      1.1  christos 			}
    589      1.1  christos 			break;
    590      1.1  christos 		case 3:
    591      1.1  christos 			if (f->f_flags & FFLAG_SIGN)
    592      1.1  christos 				rc = f->f_sg;
    593      1.1  christos 			else
    594      1.1  christos 				rc = NULL;
    595      1.1  christos 			break;
    596      1.1  christos 		}
    597      1.1  christos 
    598      1.1  christos 	DPRINTF((D_CALL|D_SIGN), "sign_get_sg(%d, %p) --> %p\n", pri, f, rc);
    599      1.1  christos 	return rc;
    600      1.1  christos }
    601      1.1  christos 
    602      1.1  christos /*
    603      1.1  christos  * create and send signature block
    604      1.2   minskim  *
    605      1.1  christos  * uses a sliding window for redundancy
    606      1.1  christos  * if force==true then simply send all available hashes, e.g. on shutdown
    607      1.2   minskim  *
    608      1.1  christos  * sliding window checks implicitly assume that new hashes are appended
    609      1.1  christos  * to the SG between two calls. if that is not the case (e.g. with repeated
    610      1.1  christos  * messages) the queue size will shrink.
    611      1.1  christos  * this has no negative consequences except generating more and shorter SBs
    612      1.1  christos  * than expected and confusing the operator because two consecutive SBs will
    613      1.1  christos  * have same FMNn
    614      1.1  christos  */
    615      1.1  christos unsigned
    616      1.1  christos sign_send_signature_block(struct signature_group_t *sg, bool force)
    617      1.1  christos {
    618      1.1  christos 	char sd[SIGN_MAX_SD_LENGTH];
    619      1.1  christos 	size_t sd_len;
    620      1.1  christos 	size_t sg_num_hashes = 0;	/* hashes in SG queue */
    621      1.1  christos 	size_t hashes_in_sb = 0;	/* number of hashes in current SB */
    622      1.1  christos 	size_t hashes_sent = 0;	/* count of hashes sent */
    623      1.1  christos 	struct string_queue *qentry, *old_qentry;
    624      1.1  christos 	struct buf_msg *buffer;
    625      1.1  christos 	struct filed_queue *fq;
    626      1.3     lukem 	size_t i;
    627      1.1  christos 
    628      1.1  christos 	if (!sg) return 0;
    629      1.1  christos 	DPRINTF((D_CALL|D_SIGN), "sign_send_signature_block(%p, %d)\n",
    630      1.1  christos 	    sg, force);
    631      1.1  christos 
    632      1.1  christos 	STAILQ_FOREACH(qentry, &sg->hashes, entries)
    633      1.1  christos 		sg_num_hashes++;
    634      1.1  christos 
    635      1.1  christos 	/* only act if a division is full */
    636      1.1  christos 	if (!sg_num_hashes
    637      1.1  christos 	    || (!force && (sg_num_hashes % SIGN_HASH_DIVISION_NUM)))
    638      1.1  christos 		return 0;
    639      1.1  christos 
    640      1.1  christos 	/* if no CB sent so far then do now, just before first SB */
    641      1.1  christos 	if (sg->resendcount == SIGN_RESENDCOUNT_CERTBLOCK)
    642      1.1  christos 		sign_send_certificate_block(sg);
    643      1.2   minskim 
    644      1.1  christos 	/* shortly after reboot we have shorter SBs */
    645      1.1  christos 	hashes_in_sb = MIN(sg_num_hashes, SIGN_HASH_NUM);
    646      1.2   minskim 
    647      1.1  christos 	DPRINTF(D_SIGN, "sign_send_signature_block(): "
    648      1.1  christos 	    "sg_num_hashes = %zu, hashes_in_sb = %zu, SIGN_HASH_NUM = %d\n",
    649      1.1  christos 	    sg_num_hashes, hashes_in_sb, SIGN_HASH_NUM);
    650      1.1  christos 	if (sg_num_hashes > SIGN_HASH_NUM) {
    651      1.1  christos 		DPRINTF(D_SIGN, "sign_send_signature_block(): sg_num_hashes"
    652      1.1  christos 		    " > SIGN_HASH_NUM -- This should not happen!\n");
    653      1.1  christos 	}
    654      1.1  christos 
    655      1.1  christos 	/* now the SD */
    656      1.1  christos 	qentry = STAILQ_FIRST(&sg->hashes);
    657      1.1  christos 	sd_len = snprintf(sd, sizeof(sd), "[ssign "
    658      1.1  christos 	    "VER=\"%s\" RSID=\"%" PRIuFAST64 "\" SG=\"%d\" "
    659      1.1  christos 	    "SPRI=\"%d\" GBC=\"%" PRIuFAST64 "\" FMN=\"%" PRIuFAST64 "\" "
    660      1.1  christos 	    "CNT=\"%zu\" HB=\"",
    661      1.1  christos 	    GlobalSign.ver, GlobalSign.rsid, GlobalSign.sg,
    662      1.1  christos 	    sg->spri, GlobalSign.gbc, qentry->key,
    663      1.1  christos 	    hashes_in_sb);
    664      1.1  christos 	while (hashes_sent < hashes_in_sb) {
    665      1.1  christos 		assert(qentry);
    666      1.1  christos 		sd_len += snprintf(sd+sd_len, sizeof(sd)-sd_len, "%s ",
    667      1.1  christos 		    qentry->data);
    668      1.1  christos 		hashes_sent++;
    669      1.1  christos 		qentry = STAILQ_NEXT(qentry, entries);
    670      1.1  christos 	}
    671      1.1  christos 	/* overwrite last space and close SD */
    672      1.1  christos 	assert(sd_len < sizeof(sd));
    673      1.1  christos 	assert(sd[sd_len] == '\0');
    674      1.1  christos 	assert(sd[sd_len-1] == ' ');
    675      1.1  christos 	sd[sd_len-1] = '\0';
    676      1.1  christos 	sd_len = strlcat(sd, "\" SIGN=\"\"]", sizeof(sd));
    677      1.1  christos 
    678      1.1  christos 	if (sign_msg_sign(&buffer, sd, sizeof(sd))) {
    679      1.1  christos 		DPRINTF((D_CALL|D_SIGN), "sign_send_signature_block(): calling"
    680      1.1  christos 		    " fprintlog(), sending %zu out of %zu hashes\n",
    681      1.1  christos 		    MIN(SIGN_MAX_HASH_NUM, sg_num_hashes), sg_num_hashes);
    682      1.2   minskim 
    683      1.1  christos 		STAILQ_FOREACH(fq, &sg->files, entries) {
    684      1.1  christos 			int tmpcnt;
    685      1.1  christos 			tmpcnt = fq->f->f_prevcount;
    686      1.1  christos 			fprintlog(fq->f, buffer, NULL);
    687      1.1  christos 			fq->f->f_prevcount = tmpcnt;
    688      1.1  christos 		}
    689      1.1  christos 		sign_inc_gbc();
    690      1.1  christos 		DELREF(buffer);
    691      1.1  christos 	}
    692      1.1  christos 	/* always drop the oldest division of hashes */
    693      1.1  christos 	if (sg_num_hashes >= SIGN_HASH_NUM) {
    694      1.1  christos 		qentry = STAILQ_FIRST(&sg->hashes);
    695      1.1  christos 		for (i = 0; i < SIGN_HASH_DIVISION_NUM; i++) {
    696      1.1  christos 			old_qentry = qentry;
    697      1.1  christos 			qentry = STAILQ_NEXT(old_qentry, entries);
    698      1.1  christos 			STAILQ_REMOVE(&sg->hashes, old_qentry,
    699      1.1  christos 			    string_queue, entries);
    700      1.1  christos 			FREEPTR(old_qentry->data);
    701      1.1  christos 			FREEPTR(old_qentry);
    702      1.1  christos 		}
    703      1.1  christos 	}
    704      1.1  christos 	return hashes_sent;
    705      1.1  christos }
    706      1.1  christos 
    707      1.1  christos void
    708      1.1  christos sign_free_hashes(struct signature_group_t *sg)
    709      1.1  christos {
    710      1.1  christos 	DPRINTF((D_CALL|D_SIGN), "sign_free_hashes(%p)\n", sg);
    711      1.1  christos 	sign_free_string_queue(&sg->hashes);
    712      1.1  christos }
    713      1.1  christos 
    714      1.1  christos void
    715      1.1  christos sign_free_string_queue(struct string_queue_head *sqhead)
    716      1.1  christos {
    717      1.1  christos 	struct string_queue *qentry, *tmp_qentry;
    718      1.2   minskim 
    719      1.1  christos 	DPRINTF((D_CALL|D_SIGN), "sign_free_string_queue(%p)\n", sqhead);
    720      1.1  christos 	STAILQ_FOREACH_SAFE(qentry, sqhead, entries, tmp_qentry) {
    721      1.1  christos 		STAILQ_REMOVE(sqhead, qentry, string_queue, entries);
    722      1.1  christos 		FREEPTR(qentry->data);
    723      1.1  christos 		free(qentry);
    724      1.1  christos 	}
    725      1.1  christos 	assert(STAILQ_EMPTY(sqhead));
    726      1.1  christos }
    727      1.1  christos 
    728      1.1  christos /*
    729      1.1  christos  * hash one syslog message
    730      1.1  christos  */
    731      1.1  christos bool
    732      1.1  christos sign_msg_hash(char *line, char **hash)
    733      1.1  christos {
    734      1.1  christos 	unsigned char md_value[EVP_MAX_MD_SIZE];
    735      1.1  christos 	unsigned char md_b64[EVP_MAX_MD_SIZE*2];
    736      1.1  christos 	/* TODO: exact expression for b64 length? */
    737      1.1  christos 	unsigned md_len = 0;
    738      1.1  christos 
    739      1.1  christos 	DPRINTF((D_CALL|D_SIGN), "sign_msg_hash('%s')\n", line);
    740      1.2   minskim 
    741      1.1  christos 	SSL_CHECK_ONE(EVP_DigestInit_ex(GlobalSign.mdctx, GlobalSign.md, NULL));
    742      1.1  christos 	SSL_CHECK_ONE(EVP_DigestUpdate(GlobalSign.mdctx, line, strlen(line)));
    743      1.1  christos 	SSL_CHECK_ONE(EVP_DigestFinal_ex(GlobalSign.mdctx, md_value, &md_len));
    744      1.2   minskim 
    745      1.1  christos 	b64_ntop(md_value, md_len, (char *)md_b64, EVP_MAX_MD_SIZE*2);
    746      1.1  christos 	*hash = strdup((char *)md_b64);
    747      1.1  christos 
    748      1.1  christos 	DPRINTF((D_CALL|D_SIGN), "sign_msg_hash() --> \"%s\"\n", *hash);
    749      1.1  christos 	return true;
    750      1.1  christos }
    751      1.1  christos 
    752      1.1  christos /*
    753      1.1  christos  * append hash to SG queue
    754      1.1  christos  */
    755      1.1  christos bool
    756      1.1  christos sign_append_hash(char *hash, struct signature_group_t *sg)
    757      1.1  christos {
    758      1.1  christos 	struct string_queue *qentry;
    759      1.1  christos 
    760      1.1  christos 	/* if one SG is shared by several destinations
    761      1.1  christos 	 * prevent duplicate entries */
    762      1.1  christos 	if ((qentry = STAILQ_LAST(&sg->hashes, string_queue, entries))
    763      1.1  christos 	    && !strcmp(qentry->data, hash)) {
    764      1.1  christos 		DPRINTF((D_CALL|D_SIGN), "sign_append_hash('%s', %p): "
    765      1.1  christos 		    "hash already in queue\n", hash, sg);
    766      1.1  christos 		return false;
    767      1.1  christos 	}
    768      1.1  christos 
    769      1.1  christos 	MALLOC(qentry, sizeof(*qentry));
    770      1.1  christos 	qentry->key = sign_assign_msg_num(sg);
    771      1.1  christos 	qentry->data = hash;
    772      1.1  christos 	STAILQ_INSERT_TAIL(&sg->hashes, qentry, entries);
    773      1.1  christos 	DPRINTF((D_CALL|D_SIGN), "sign_append_hash('%s', %p): "
    774      1.1  christos 	    "#%" PRIdFAST64 "\n", hash, sg, qentry->key);
    775      1.1  christos 	return true;
    776      1.1  christos }
    777      1.1  christos 
    778      1.1  christos /*
    779      1.1  christos  * sign one syslog-sign message
    780      1.2   minskim  *
    781      1.1  christos  * requires a ssign or ssigt-cert SD element
    782      1.1  christos  * ending with ' SIGN=""]' in sd
    783      1.1  christos  * linesize is available memory (= sizeof(sd))
    784      1.2   minskim  *
    785      1.1  christos  * function will calculate signature and return a new buffer
    786      1.1  christos  */
    787      1.1  christos bool
    788      1.1  christos sign_msg_sign(struct buf_msg **bufferptr, char *sd, size_t linesize)
    789      1.1  christos {
    790      1.1  christos 	char *signature, *line;
    791      1.1  christos 	size_t linelen, tlsprefixlen, endptr, newlinelen;
    792      1.1  christos 	struct buf_msg *buffer;
    793      1.1  christos 
    794      1.1  christos 	DPRINTF((D_CALL|D_SIGN), "sign_msg_sign()\n");
    795      1.1  christos 	endptr = strlen(sd);
    796      1.1  christos 
    797      1.1  christos 	assert(endptr < linesize);
    798      1.1  christos 	assert(sd[endptr] == '\0');
    799      1.1  christos 	assert(sd[endptr-1] == ']');
    800      1.1  christos 	assert(sd[endptr-2] == '"');
    801      1.1  christos 
    802      1.1  christos 	/* set up buffer */
    803      1.1  christos 	buffer = buf_msg_new(0);
    804      1.1  christos 	buffer->timestamp = strdup(make_timestamp(NULL, !BSDOutputFormat));
    805      1.1  christos 	buffer->prog = appname;
    806      1.1  christos 	buffer->pid = include_pid;
    807      1.1  christos 	buffer->recvhost = buffer->host = LocalFQDN;
    808      1.1  christos 	buffer->pri = 110;
    809      1.1  christos 	buffer->flags = IGN_CONS|SIGN_MSG;
    810      1.1  christos 	buffer->sd = sd;
    811      1.1  christos 
    812      1.1  christos 	/* SD ready, now format and sign */
    813      1.1  christos 	if (!format_buffer(buffer, &line, &linelen, NULL,
    814      1.1  christos 		&tlsprefixlen, NULL)) {
    815      1.1  christos 		DPRINTF((D_CALL|D_SIGN), "sign_send_signature_block():"
    816      1.1  christos 		    " format_buffer() failed\n");
    817      1.1  christos 		buffer->sd = NULL;
    818      1.1  christos 		DELREF(buffer);
    819      1.1  christos 		return false;
    820      1.1  christos 	}
    821      1.1  christos 	if (!sign_string_sign(line+tlsprefixlen, &signature)) {
    822      1.1  christos 		DPRINTF((D_CALL|D_SIGN), "sign_send_signature_block():"
    823      1.1  christos 		    " sign_string_sign() failed\n");
    824      1.1  christos 		buffer->sd = NULL;
    825      1.1  christos 		DELREF(buffer);
    826      1.1  christos 		FREEPTR(line);
    827      1.1  christos 		return false;
    828      1.1  christos 	}
    829      1.1  christos 	FREEPTR(line);
    830      1.1  christos 	sd[endptr-2] = '\0';
    831      1.1  christos 	newlinelen = strlcat(sd, signature, linesize);
    832      1.1  christos 	newlinelen = strlcat(sd, "\"]", linesize);
    833      1.2   minskim 
    834      1.1  christos 	if (newlinelen >= linesize) {
    835      1.1  christos 		DPRINTF(D_SIGN, "sign_send_signature_block(): "
    836      1.1  christos 		    "buffer too small\n");
    837      1.1  christos 		buffer->sd = NULL;
    838      1.1  christos 		DELREF(buffer);
    839      1.1  christos 		return false;
    840      1.1  christos 	}
    841      1.1  christos 	assert(newlinelen < linesize);
    842      1.1  christos 	assert(sd[newlinelen] == '\0');
    843      1.1  christos 	assert(sd[newlinelen-1] == ']');
    844      1.1  christos 	assert(sd[newlinelen-2] == '"');
    845      1.1  christos 
    846      1.1  christos 	buffer->sd = strdup(sd);
    847      1.1  christos 	*bufferptr = buffer;
    848      1.1  christos 	return true;
    849      1.1  christos }
    850      1.1  christos 
    851      1.1  christos /*
    852      1.1  christos  * sign one string
    853      1.1  christos  */
    854      1.1  christos bool
    855      1.1  christos sign_string_sign(char *line, char **signature)
    856      1.1  christos {
    857      1.1  christos 	char buf[SIGN_MAX_LENGTH+1];
    858      1.1  christos 	unsigned char sig_value[SIGN_B64SIGLEN_DSS];
    859      1.1  christos 	unsigned char sig_b64[SIGN_B64SIGLEN_DSS];
    860      1.1  christos 	unsigned sig_len = 0;
    861      1.1  christos 	char *p, *q;
    862      1.2   minskim 	/*
    863      1.1  christos 	 * The signature is calculated over the completely formatted
    864      1.1  christos 	 * syslog-message, including all of the PRI, HEADER, and hashes
    865      1.1  christos 	 * in the hash block, excluding spaces between fields, and also
    866      1.1  christos 	 * excluding the signature field (SD Parameter Name "SIGN", "=",
    867      1.1  christos 	 * and corresponding value).
    868      1.2   minskim 	 *
    869      1.1  christos 	 * -- I am not quite sure which spaces are to be removed.
    870      1.1  christos 	 * Only the ones inside the "ssign" element or those between
    871      1.1  christos 	 * header fields as well?
    872      1.1  christos 	 */
    873      1.1  christos 	/* removes the string ' SIGN=""' */
    874      1.1  christos 	for (p = line, q = buf;
    875      1.1  christos 	     *p && (q - buf <= SIGN_MAX_LENGTH);) {
    876      1.1  christos 		if (strncmp(p, " SIGN=\"\"", 8) == 0)
    877      1.1  christos 			p += 8;
    878      1.1  christos 		*q++ = *p++;
    879      1.1  christos 	}
    880      1.1  christos 	*q = '\0';
    881      1.1  christos 
    882      1.1  christos 	SSL_CHECK_ONE(EVP_SignInit(GlobalSign.sigctx, GlobalSign.sig));
    883      1.1  christos 	SSL_CHECK_ONE(EVP_SignUpdate(GlobalSign.sigctx, buf, q-buf));
    884      1.1  christos 	assert(GlobalSign.privkey);
    885      1.1  christos 	SSL_CHECK_ONE(EVP_SignFinal(GlobalSign.sigctx, sig_value, &sig_len,
    886      1.1  christos 	    GlobalSign.privkey));
    887      1.2   minskim 
    888      1.1  christos 	b64_ntop(sig_value, sig_len, (char *)sig_b64, sizeof(sig_b64));
    889      1.1  christos 	*signature = strdup((char *)sig_b64);
    890      1.1  christos 
    891      1.1  christos 	DPRINTF((D_CALL|D_SIGN), "sign_string_sign('%s') --> '%s'\n",
    892      1.1  christos 	    buf, *signature);
    893      1.1  christos 	return *signature != NULL;
    894      1.1  christos }
    895      1.1  christos 
    896      1.1  christos void
    897  1.3.8.2      yamt sign_new_reboot_session(void)
    898      1.1  christos {
    899      1.1  christos 	struct signature_group_t *sg;
    900      1.1  christos 
    901      1.1  christos 	DPRINTF((D_CALL|D_SIGN), "sign_new_reboot_session()\n");
    902      1.1  christos 
    903      1.1  christos 	/* global counters */
    904      1.1  christos 	GlobalSign.gbc = 0;
    905      1.1  christos 	/* might be useful for later analysis:
    906      1.1  christos 	 * rebooted session IDs are sequential,
    907      1.1  christos 	 * normal IDs are almost always not */
    908      1.1  christos 	GlobalSign.rsid++;
    909      1.1  christos 
    910      1.1  christos 	assert(GlobalSign.sg <= 3);
    911      1.1  christos 	/* reset SGs */
    912      1.1  christos 	STAILQ_FOREACH(sg, &GlobalSign.SigGroups, entries) {
    913      1.1  christos 		sg->resendcount = SIGN_RESENDCOUNT_CERTBLOCK;
    914      1.1  christos 		sg->last_msg_num = 1;
    915      1.1  christos 	}
    916      1.1  christos }
    917      1.1  christos 
    918      1.1  christos /* get msg_num, increment counter, check overflow */
    919      1.1  christos uint_fast64_t
    920      1.1  christos sign_assign_msg_num(struct signature_group_t *sg)
    921      1.1  christos {
    922      1.1  christos 	uint_fast64_t old;
    923      1.1  christos 
    924      1.1  christos 	old = sg->last_msg_num++;
    925      1.1  christos 	if (sg->last_msg_num > SIGN_MAX_COUNT)
    926      1.1  christos 		sign_new_reboot_session();
    927      1.1  christos 	return old;
    928      1.1  christos }
    929      1.1  christos 
    930      1.1  christos 
    931      1.1  christos /* increment gbc, check overflow */
    932      1.1  christos void
    933  1.3.8.2      yamt sign_inc_gbc(void)
    934      1.1  christos {
    935      1.1  christos 	if (++GlobalSign.gbc > SIGN_MAX_COUNT)
    936      1.1  christos 		sign_new_reboot_session();
    937      1.1  christos }
    938      1.1  christos #endif /* !DISABLE_SIGN */
    939