1 /* $NetBSD: sign.c,v 1.2 2008/11/07 07:36:38 minskim Exp $ */
4 * Copyright (c) 2008 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
40 * syslog-sign related code for syslogd
45 * Issues with the current internet draft:
46 * 1. The draft is a bit unclear on the input format for the signature,
47 * so this might have to be changed later. Cf. sign_string_sign()
48 * 2. The draft only defines DSA signatures. I hope it will be extended
49 * to DSS, thus allowing DSA, RSA (ANSI X9.31) and ECDSA (ANSI X9.62)
50 * 3. The draft does not define the data format for public keys in CBs.
51 * This implementation sends public keys in DER encoding.
52 * 4. This current implementation uses high-level OpenSSL API.
53 * I am not sure if these completely implement the FIPS/ANSI standards.
54 * Update after WG discussion in August:
55 * 1. check; next draft will be clearer and specify the format as implemented.
56 * 2. check; definitely only DSA in this version.
57 * 3. remains a problem, so far no statement from authors or WG.
58 * 4. check; used EVP_dss1 method implements FIPS.
61 * Limitations of this implementation:
62 * - cannot use OpenPGP keys, only PKIX or DSA due to OpenSSL capabilities
63 * - only works for correctly formatted messages, because incorrect messages
64 * are reformatted (e.g. if it receives a message with two spaces between
65 * fields it might even be parsed, but the output will have only one space).
68 #include <sys/cdefs.h>
69 __RCSID("$NetBSD: sign.c,v 1.2 2008/11/07 07:36:38 minskim Exp $");
75 #endif /* !DISABLE_TLS */
80 * init all SGs for a given algorithm
83 sign_global_init(struct filed
*Files
)
85 DPRINTF((D_CALL
|D_SIGN
), "sign_global_init()\n");
86 if (!(GlobalSign
.sg
== 0 || GlobalSign
.sg
== 1
87 || GlobalSign
.sg
== 2 || GlobalSign
.sg
== 3)) {
88 logerror("sign_init(): invalid SG %d", GlobalSign
.sg
);
95 /* signature algorithm */
96 /* can probably be merged with the hash algorithm/context but
97 * I leave the optimization for later until the RFC is ready */
98 GlobalSign
.sigctx
= EVP_MD_CTX_create();
99 EVP_MD_CTX_init(GlobalSign
.sigctx
);
101 /* the signature algorithm depends on the type of key */
102 if (EVP_PKEY_DSA
== EVP_PKEY_type(GlobalSign
.pubkey
->type
)) {
103 GlobalSign
.sig
= EVP_dss1();
104 GlobalSign
.sig_len_b64
= SIGN_B64SIGLEN_DSS
;
105 /* this is the place to add non-DSA key types and algorithms
106 } else if (EVP_PKEY_RSA == EVP_PKEY_type(GlobalSign.pubkey->type)) {
107 GlobalSign.sig = EVP_sha1();
108 GlobalSign.sig_len_b64 = 28;
111 logerror("key type not supported for syslog-sign");
115 assert(GlobalSign
.keytype
== 'C' || GlobalSign
.keytype
== 'K');
116 assert(GlobalSign
.pubkey_b64
&& GlobalSign
.privkey
&&
118 assert(GlobalSign
.privkey
->pkey
.dsa
->priv_key
);
121 STAILQ_INIT(&GlobalSign
.SigGroups
);
124 OpenSSL_add_all_digests();
125 GlobalSign
.mdctx
= EVP_MD_CTX_create();
126 EVP_MD_CTX_init(GlobalSign
.mdctx
);
128 /* values for SHA-1 */
129 GlobalSign
.md
= EVP_dss1();
130 GlobalSign
.md_len_b64
= 28;
131 GlobalSign
.ver
= "0111";
133 if (!sign_sg_init(Files
))
135 sign_new_reboot_session();
137 DPRINTF(D_SIGN
, "length values: SIGN_MAX_SD_LENGTH %d, "
138 "SIGN_MAX_FRAG_LENGTH %d, SIGN_MAX_SB_LENGTH %d, "
139 "SIGN_MAX_HASH_NUM %d\n", SIGN_MAX_SD_LENGTH
,
140 SIGN_MAX_FRAG_LENGTH
, SIGN_MAX_SB_LENGTH
, SIGN_MAX_HASH_NUM
);
142 /* set just before return, so it indicates initialization */
143 GlobalSign
.rsid
= now
;
148 * get keys for syslog-sign
149 * either from the X.509 certificate used for TLS
150 * or by generating a new one
152 * sets the global variables
153 * GlobalSign.keytype, GlobalSign.pubkey_b64,
154 * GlobalSign.privkey, and GlobalSign.pubkey
159 EVP_PKEY
*pubkey
= NULL
, *privkey
= NULL
;
160 unsigned char *der_pubkey
= NULL
, *ptr_der_pubkey
= NULL
;
161 char *pubkey_b64
= NULL
;
164 /* try PKIX/TLS key first */
167 if (tls_opt
.global_TLS_CTX
168 && (ssl
= SSL_new(tls_opt
.global_TLS_CTX
))) {
170 DPRINTF(D_SIGN
, "Try to get keys from TLS X.509 cert...\n");
172 if (!(cert
= SSL_get_certificate(ssl
))) {
173 logerror("SSL_get_certificate() failed");
177 if (!(privkey
= SSL_get_privatekey(ssl
))) {
178 logerror("SSL_get_privatekey() failed");
182 if (!(pubkey
= X509_get_pubkey(cert
))) {
183 logerror("X509_get_pubkey() failed");
188 * - privkey is just a pointer into SSL_CTX and
189 * must not be changed nor be free()d
190 * - but pubkey has to be freed with EVP_PKEY_free()
194 if (EVP_PKEY_DSA
!= EVP_PKEY_type(pubkey
->type
)) {
195 DPRINTF(D_SIGN
, "X.509 cert has no DSA key\n");
196 EVP_PKEY_free(pubkey
);
200 DPRINTF(D_SIGN
, "Got public and private key "
201 "from X.509 --> use type PKIX\n");
202 GlobalSign
.keytype
= 'C';
203 GlobalSign
.privkey
= privkey
;
204 GlobalSign
.pubkey
= pubkey
;
206 /* base64 certificate encoding */
207 der_len
= i2d_X509(cert
, NULL
);
208 if (!(ptr_der_pubkey
= der_pubkey
= malloc(der_len
))
209 || !(pubkey_b64
= malloc(der_len
*2))) {
211 logerror("malloc() failed");
214 if (i2d_X509(cert
, &ptr_der_pubkey
) <= 0) {
215 logerror("i2d_X509() failed");
218 b64_ntop(der_pubkey
, der_len
, pubkey_b64
, der_len
*2);
220 /* try to resize memory object as needed */
221 GlobalSign
.pubkey_b64
= realloc(pubkey_b64
,
222 strlen(pubkey_b64
)+1);
223 if (!GlobalSign
.pubkey_b64
)
224 GlobalSign
.pubkey_b64
= pubkey_b64
;
227 #endif /* !DISABLE_TLS */
228 if (!(privkey
&& pubkey
)) { /* PKIX not available --> generate key */
231 DPRINTF(D_SIGN
, "Unable to get keys from X.509 "
232 "--> use DSA with type 'K'\n");
233 if (!(privkey
= EVP_PKEY_new())) {
234 logerror("EVP_PKEY_new() failed");
237 dsa
= DSA_generate_parameters(SIGN_GENCERT_BITS
, NULL
, 0,
238 NULL
, NULL
, NULL
, NULL
);
239 if (!DSA_generate_key(dsa
)) {
240 logerror("DSA_generate_key() failed");
243 if (!EVP_PKEY_assign_DSA(privkey
, dsa
)) {
244 logerror("EVP_PKEY_assign_DSA() failed");
247 GlobalSign
.keytype
= 'K'; /* public/private keys used */
248 GlobalSign
.privkey
= privkey
;
249 GlobalSign
.pubkey
= privkey
;
251 /* pubkey base64 encoding */
252 der_len
= i2d_DSA_PUBKEY(dsa
, NULL
);
253 if (!(ptr_der_pubkey
= der_pubkey
= malloc(der_len
))
254 || !(pubkey_b64
= malloc(der_len
*2))) {
256 logerror("malloc() failed");
259 if (i2d_DSA_PUBKEY(dsa
, &ptr_der_pubkey
) <= 0) {
260 logerror("i2d_DSA_PUBKEY() failed");
263 b64_ntop(der_pubkey
, der_len
, pubkey_b64
, der_len
*2);
265 /* try to resize memory object as needed */
266 GlobalSign
.pubkey_b64
= realloc(pubkey_b64
,
267 strlen(pubkey_b64
) + 1);
268 if (!GlobalSign
.pubkey_b64
)
269 GlobalSign
.pubkey_b64
= pubkey_b64
;
278 sign_sg_init(struct filed
*Files
)
280 struct signature_group_t
*sg
, *newsg
, *last_sg
;
281 struct filed_queue
*fq
;
282 struct string_queue
*sqentry
, *last_sqentry
;
286 /* note on SG 1 and 2:
287 * it is assumed that redundant signature groups
288 * and especially signature groups without an associated
289 * destination are harmless.
290 * this currently holds true because sign_append_hash()
291 * is called from fprintlog(), so only actually used
292 * signature group get hashes and need memory for them
294 /* possible optimization for SGs 1 and 2:
295 * use a struct signature_group_t *newsg[IETF_NUM_PRIVALUES]
296 * for direct group lookup
299 #define ALLOC_OR_FALSE(x) do { \
300 if(!((x) = calloc(1, sizeof(*(x))))) { \
301 logerror("Unable to allocate memory"); \
304 } while (/*CONSTCOND*/0)
306 #define ALLOC_SG(x) do { \
308 (x)->last_msg_num = 1; /* cf. section 4.2.5 */ \
309 STAILQ_INIT(&(x)->hashes); \
310 STAILQ_INIT(&(x)->files); \
311 } while (/*CONSTCOND*/0)
313 /* alloc(fq) and add to SGs file queue */
314 #define ASSIGN_FQ() do { \
315 ALLOC_OR_FALSE(fq); \
318 DPRINTF(D_SIGN, "SG@%p <--> f@%p\n", newsg, f); \
319 STAILQ_INSERT_TAIL(&newsg->files, fq, entries); \
320 } while (/*CONSTCOND*/0)
322 switch (GlobalSign
.sg
) {
324 /* one SG, linked to all files */
327 for (f
= Files
; f
; f
= f
->f_next
)
329 STAILQ_INSERT_TAIL(&GlobalSign
.SigGroups
,
333 /* every PRI gets one SG */
334 for (i
= 0; i
< IETF_NUM_PRIVALUES
; i
++) {
341 /* now find all destinations associated with this SG */
342 for (f
= Files
; f
; f
= f
->f_next
)
343 /* check priorities */
344 if (MATCH_PRI(f
, fac
, prilev
))
346 STAILQ_INSERT_TAIL(&GlobalSign
.SigGroups
,
351 /* PRI ranges get one SG, boundaries given by the
352 * SPRI, indicating the largest PRI in the SG
354 * either GlobalSign.sig2_delims has a list of
355 * user configured delimiters, or we use a default
356 * and set up one SG per facility
358 if (STAILQ_EMPTY(&GlobalSign
.sig2_delims
)) {
359 DPRINTF(D_SIGN
, "sign_sg_init(): set default "
360 "values for SG 2\n");
361 for (i
= 0; i
< (IETF_NUM_PRIVALUES
>>3); i
++) {
362 ALLOC_OR_FALSE(sqentry
);
363 sqentry
->data
= NULL
;
364 sqentry
->key
= (i
<<3);
365 STAILQ_INSERT_TAIL(&GlobalSign
.sig2_delims
,
369 assert(!STAILQ_EMPTY(&GlobalSign
.sig2_delims
));
371 /* add one more group at the end */
372 last_sqentry
= STAILQ_LAST(&GlobalSign
.sig2_delims
,
373 string_queue
, entries
);
374 if (last_sqentry
->key
< IETF_NUM_PRIVALUES
) {
375 ALLOC_OR_FALSE(sqentry
);
376 sqentry
->data
= NULL
;
377 sqentry
->key
= IETF_NUM_PRIVALUES
-1;
378 STAILQ_INSERT_TAIL(&GlobalSign
.sig2_delims
,
382 STAILQ_FOREACH(sqentry
, &GlobalSign
.sig2_delims
, entries
) {
383 unsigned int min_pri
= 0;
385 newsg
->spri
= sqentry
->key
;
387 /* check _all_ priorities in SG */
388 last_sg
= STAILQ_LAST(&GlobalSign
.SigGroups
,
389 signature_group_t
, entries
);
391 min_pri
= last_sg
->spri
+ 1;
393 DPRINTF(D_SIGN
, "sign_sg_init(): add SG@%p: SG=\"2\","
394 " SPRI=\"%d\" -- for msgs with "
396 newsg
, newsg
->spri
, min_pri
, newsg
->spri
);
397 /* now find all destinations associated with this SG */
398 for (f
= Files
; f
; f
= f
->f_next
) {
400 for (i
= min_pri
; i
<= newsg
->spri
; i
++) {
404 if (MATCH_PRI(f
, fac
, prilev
)) {
412 STAILQ_INSERT_TAIL(&GlobalSign
.SigGroups
,
417 /* every file (with flag) gets one SG */
418 for (f
= Files
; f
; f
= f
->f_next
) {
419 if (!(f
->f_flags
& FFLAG_SIGN
)) {
424 newsg
->spri
= f
->f_file
; /* not needed but shows SGs */
426 STAILQ_INSERT_TAIL(&GlobalSign
.SigGroups
,
431 DPRINTF((D_PARSE
|D_SIGN
), "sign_sg_init() set up these "
432 "Signature Groups:\n");
433 STAILQ_FOREACH(sg
, &GlobalSign
.SigGroups
, entries
) {
434 DPRINTF((D_PARSE
|D_SIGN
), "SG@%p with SG=\"%d\", SPRI=\"%d\","
435 " associated files:\n", sg
, GlobalSign
.sg
, sg
->spri
);
436 STAILQ_FOREACH(fq
, &sg
->files
, entries
) {
437 DPRINTF((D_PARSE
|D_SIGN
), " f@%p with type %d\n",
438 fq
->f
, fq
->f
->f_type
);
445 * free all SGs for a given algorithm
450 struct signature_group_t
*sg
, *tmp_sg
;
451 struct filed_queue
*fq
, *tmp_fq
;
453 DPRINTF((D_CALL
|D_SIGN
), "sign_global_free()\n");
454 STAILQ_FOREACH_SAFE(sg
, &GlobalSign
.SigGroups
, entries
, tmp_sg
) {
455 if (!STAILQ_EMPTY(&sg
->hashes
)) {
456 /* send CB and SB twice to get minimal redundancy
457 * for the last few message hashes */
458 sign_send_certificate_block(sg
);
459 sign_send_certificate_block(sg
);
460 sign_send_signature_block(sg
, true);
461 sign_send_signature_block(sg
, true);
462 sign_free_hashes(sg
);
464 fq
= STAILQ_FIRST(&sg
->files
);
466 tmp_fq
= STAILQ_NEXT(fq
, entries
);
470 STAILQ_REMOVE(&GlobalSign
.SigGroups
,
471 sg
, signature_group_t
, entries
);
474 sign_free_string_queue(&GlobalSign
.sig2_delims
);
476 if (GlobalSign
.privkey
) {
477 GlobalSign
.privkey
= NULL
;
479 if (GlobalSign
.pubkey
) {
480 EVP_PKEY_free(GlobalSign
.pubkey
);
481 GlobalSign
.pubkey
= NULL
;
483 if(GlobalSign
.mdctx
) {
484 EVP_MD_CTX_destroy(GlobalSign
.mdctx
);
485 GlobalSign
.mdctx
= NULL
;
487 if(GlobalSign
.sigctx
) {
488 EVP_MD_CTX_destroy(GlobalSign
.sigctx
);
489 GlobalSign
.sigctx
= NULL
;
491 FREEPTR(GlobalSign
.pubkey_b64
);
495 * create and send certificate block
498 sign_send_certificate_block(struct signature_group_t
*sg
)
500 struct filed_queue
*fq
;
501 struct buf_msg
*buffer
;
503 char payload
[SIGN_MAX_PAYLOAD_LENGTH
];
504 char sd
[SIGN_MAX_SD_LENGTH
];
505 size_t payload_len
, sd_len
, fragment_len
;
506 size_t payload_index
= 0;
508 /* do nothing if CBs already sent or if there was no message in SG */
510 || ((sg
->resendcount
== SIGN_RESENDCOUNT_CERTBLOCK
)
511 && STAILQ_EMPTY(&sg
->hashes
)))
514 DPRINTF((D_CALL
|D_SIGN
), "sign_send_certificate_block(%p)\n", sg
);
515 tstamp
= make_timestamp(NULL
, true);
517 payload_len
= snprintf(payload
, sizeof(payload
), "%s %c %s", tstamp
,
518 GlobalSign
.keytype
, GlobalSign
.pubkey_b64
);
519 if (payload_len
>= sizeof(payload
)) {
520 DPRINTF(D_SIGN
, "Buffer too small for syslog-sign setup\n");
524 while (payload_index
< payload_len
) {
525 if (payload_len
- payload_index
<= SIGN_MAX_FRAG_LENGTH
)
526 fragment_len
= payload_len
- payload_index
;
528 fragment_len
= SIGN_MAX_FRAG_LENGTH
;
531 sd_len
= snprintf(sd
, sizeof(sd
), "[ssign-cert "
532 "VER=\"%s\" RSID=\"%" PRIuFAST64
"\" SG=\"%d\" "
533 "SPRI=\"%d\" TBPL=\"%zu\" INDEX=\"%zu\" "
534 "FLEN=\"%zu\" FRAG=\"%.*s\" "
536 GlobalSign
.ver
, GlobalSign
.rsid
, GlobalSign
.sg
,
537 sg
->spri
, payload_len
, payload_index
+1,
538 fragment_len
, (int)fragment_len
,
539 &payload
[payload_index
]);
540 assert(sd_len
< sizeof(sd
));
541 assert(sd
[sd_len
] == '\0');
542 assert(sd
[sd_len
-1] == ']');
543 assert(sd
[sd_len
-2] == '"');
545 if (!sign_msg_sign(&buffer
, sd
, sizeof(sd
)))
547 DPRINTF((D_CALL
|D_SIGN
), "sign_send_certificate_block(): "
548 "calling fprintlog()\n");
550 STAILQ_FOREACH(fq
, &sg
->files
, entries
) {
551 /* we have to preserve the f_prevcount */
553 tmpcnt
= fq
->f
->f_prevcount
;
554 fprintlog(fq
->f
, buffer
, NULL
);
555 fq
->f
->f_prevcount
= tmpcnt
;
559 payload_index
+= fragment_len
;
566 * determine the SG for a message
567 * returns NULL if -sign not configured or no SG for this priority
569 struct signature_group_t
*
570 sign_get_sg(int pri
, struct filed
*f
)
572 struct signature_group_t
*sg
, *rc
= NULL
;
574 if (GlobalSign
.rsid
&& f
)
575 switch (GlobalSign
.sg
) {
581 STAILQ_FOREACH(sg
, &GlobalSign
.SigGroups
, entries
) {
582 if (sg
->spri
>= (unsigned int)pri
) {
589 if (f
->f_flags
& FFLAG_SIGN
)
596 DPRINTF((D_CALL
|D_SIGN
), "sign_get_sg(%d, %p) --> %p\n", pri
, f
, rc
);
601 * create and send signature block
603 * uses a sliding window for redundancy
604 * if force==true then simply send all available hashes, e.g. on shutdown
606 * sliding window checks implicitly assume that new hashes are appended
607 * to the SG between two calls. if that is not the case (e.g. with repeated
608 * messages) the queue size will shrink.
609 * this has no negative consequences except generating more and shorter SBs
610 * than expected and confusing the operator because two consecutive SBs will
614 sign_send_signature_block(struct signature_group_t
*sg
, bool force
)
616 char sd
[SIGN_MAX_SD_LENGTH
];
618 size_t sg_num_hashes
= 0; /* hashes in SG queue */
619 size_t hashes_in_sb
= 0; /* number of hashes in current SB */
620 size_t hashes_sent
= 0; /* count of hashes sent */
621 struct string_queue
*qentry
, *old_qentry
;
622 struct buf_msg
*buffer
;
623 struct filed_queue
*fq
;
627 DPRINTF((D_CALL
|D_SIGN
), "sign_send_signature_block(%p, %d)\n",
630 STAILQ_FOREACH(qentry
, &sg
->hashes
, entries
)
633 /* only act if a division is full */
635 || (!force
&& (sg_num_hashes
% SIGN_HASH_DIVISION_NUM
)))
638 /* if no CB sent so far then do now, just before first SB */
639 if (sg
->resendcount
== SIGN_RESENDCOUNT_CERTBLOCK
)
640 sign_send_certificate_block(sg
);
642 /* shortly after reboot we have shorter SBs */
643 hashes_in_sb
= MIN(sg_num_hashes
, SIGN_HASH_NUM
);
645 DPRINTF(D_SIGN
, "sign_send_signature_block(): "
646 "sg_num_hashes = %zu, hashes_in_sb = %zu, SIGN_HASH_NUM = %d\n",
647 sg_num_hashes
, hashes_in_sb
, SIGN_HASH_NUM
);
648 if (sg_num_hashes
> SIGN_HASH_NUM
) {
649 DPRINTF(D_SIGN
, "sign_send_signature_block(): sg_num_hashes"
650 " > SIGN_HASH_NUM -- This should not happen!\n");
654 qentry
= STAILQ_FIRST(&sg
->hashes
);
655 sd_len
= snprintf(sd
, sizeof(sd
), "[ssign "
656 "VER=\"%s\" RSID=\"%" PRIuFAST64
"\" SG=\"%d\" "
657 "SPRI=\"%d\" GBC=\"%" PRIuFAST64
"\" FMN=\"%" PRIuFAST64
"\" "
659 GlobalSign
.ver
, GlobalSign
.rsid
, GlobalSign
.sg
,
660 sg
->spri
, GlobalSign
.gbc
, qentry
->key
,
662 while (hashes_sent
< hashes_in_sb
) {
664 sd_len
+= snprintf(sd
+sd_len
, sizeof(sd
)-sd_len
, "%s ",
667 qentry
= STAILQ_NEXT(qentry
, entries
);
669 /* overwrite last space and close SD */
670 assert(sd_len
< sizeof(sd
));
671 assert(sd
[sd_len
] == '\0');
672 assert(sd
[sd_len
-1] == ' ');
674 sd_len
= strlcat(sd
, "\" SIGN=\"\"]", sizeof(sd
));
676 if (sign_msg_sign(&buffer
, sd
, sizeof(sd
))) {
677 DPRINTF((D_CALL
|D_SIGN
), "sign_send_signature_block(): calling"
678 " fprintlog(), sending %zu out of %zu hashes\n",
679 MIN(SIGN_MAX_HASH_NUM
, sg_num_hashes
), sg_num_hashes
);
681 STAILQ_FOREACH(fq
, &sg
->files
, entries
) {
683 tmpcnt
= fq
->f
->f_prevcount
;
684 fprintlog(fq
->f
, buffer
, NULL
);
685 fq
->f
->f_prevcount
= tmpcnt
;
690 /* always drop the oldest division of hashes */
691 if (sg_num_hashes
>= SIGN_HASH_NUM
) {
692 qentry
= STAILQ_FIRST(&sg
->hashes
);
693 for (i
= 0; i
< SIGN_HASH_DIVISION_NUM
; i
++) {
695 qentry
= STAILQ_NEXT(old_qentry
, entries
);
696 STAILQ_REMOVE(&sg
->hashes
, old_qentry
,
697 string_queue
, entries
);
698 FREEPTR(old_qentry
->data
);
706 sign_free_hashes(struct signature_group_t
*sg
)
708 DPRINTF((D_CALL
|D_SIGN
), "sign_free_hashes(%p)\n", sg
);
709 sign_free_string_queue(&sg
->hashes
);
713 sign_free_string_queue(struct string_queue_head
*sqhead
)
715 struct string_queue
*qentry
, *tmp_qentry
;
717 DPRINTF((D_CALL
|D_SIGN
), "sign_free_string_queue(%p)\n", sqhead
);
718 STAILQ_FOREACH_SAFE(qentry
, sqhead
, entries
, tmp_qentry
) {
719 STAILQ_REMOVE(sqhead
, qentry
, string_queue
, entries
);
720 FREEPTR(qentry
->data
);
723 assert(STAILQ_EMPTY(sqhead
));
727 * hash one syslog message
730 sign_msg_hash(char *line
, char **hash
)
732 unsigned char md_value
[EVP_MAX_MD_SIZE
];
733 unsigned char md_b64
[EVP_MAX_MD_SIZE
*2];
734 /* TODO: exact expression for b64 length? */
737 DPRINTF((D_CALL
|D_SIGN
), "sign_msg_hash('%s')\n", line
);
739 SSL_CHECK_ONE(EVP_DigestInit_ex(GlobalSign
.mdctx
, GlobalSign
.md
, NULL
));
740 SSL_CHECK_ONE(EVP_DigestUpdate(GlobalSign
.mdctx
, line
, strlen(line
)));
741 SSL_CHECK_ONE(EVP_DigestFinal_ex(GlobalSign
.mdctx
, md_value
, &md_len
));
743 b64_ntop(md_value
, md_len
, (char *)md_b64
, EVP_MAX_MD_SIZE
*2);
744 *hash
= strdup((char *)md_b64
);
746 DPRINTF((D_CALL
|D_SIGN
), "sign_msg_hash() --> \"%s\"\n", *hash
);
751 * append hash to SG queue
754 sign_append_hash(char *hash
, struct signature_group_t
*sg
)
756 struct string_queue
*qentry
;
758 /* if one SG is shared by several destinations
759 * prevent duplicate entries */
760 if ((qentry
= STAILQ_LAST(&sg
->hashes
, string_queue
, entries
))
761 && !strcmp(qentry
->data
, hash
)) {
762 DPRINTF((D_CALL
|D_SIGN
), "sign_append_hash('%s', %p): "
763 "hash already in queue\n", hash
, sg
);
767 MALLOC(qentry
, sizeof(*qentry
));
768 qentry
->key
= sign_assign_msg_num(sg
);
770 STAILQ_INSERT_TAIL(&sg
->hashes
, qentry
, entries
);
771 DPRINTF((D_CALL
|D_SIGN
), "sign_append_hash('%s', %p): "
772 "#%" PRIdFAST64
"\n", hash
, sg
, qentry
->key
);
777 * sign one syslog-sign message
779 * requires a ssign or ssigt-cert SD element
780 * ending with ' SIGN=""]' in sd
781 * linesize is available memory (= sizeof(sd))
783 * function will calculate signature and return a new buffer
786 sign_msg_sign(struct buf_msg
**bufferptr
, char *sd
, size_t linesize
)
788 char *signature
, *line
;
789 size_t linelen
, tlsprefixlen
, endptr
, newlinelen
;
790 struct buf_msg
*buffer
;
792 DPRINTF((D_CALL
|D_SIGN
), "sign_msg_sign()\n");
795 assert(endptr
< linesize
);
796 assert(sd
[endptr
] == '\0');
797 assert(sd
[endptr
-1] == ']');
798 assert(sd
[endptr
-2] == '"');
801 buffer
= buf_msg_new(0);
802 buffer
->timestamp
= strdup(make_timestamp(NULL
, !BSDOutputFormat
));
803 buffer
->prog
= appname
;
804 buffer
->pid
= include_pid
;
805 buffer
->recvhost
= buffer
->host
= LocalFQDN
;
807 buffer
->flags
= IGN_CONS
|SIGN_MSG
;
810 /* SD ready, now format and sign */
811 if (!format_buffer(buffer
, &line
, &linelen
, NULL
,
812 &tlsprefixlen
, NULL
)) {
813 DPRINTF((D_CALL
|D_SIGN
), "sign_send_signature_block():"
814 " format_buffer() failed\n");
819 if (!sign_string_sign(line
+tlsprefixlen
, &signature
)) {
820 DPRINTF((D_CALL
|D_SIGN
), "sign_send_signature_block():"
821 " sign_string_sign() failed\n");
829 newlinelen
= strlcat(sd
, signature
, linesize
);
830 newlinelen
= strlcat(sd
, "\"]", linesize
);
832 if (newlinelen
>= linesize
) {
833 DPRINTF(D_SIGN
, "sign_send_signature_block(): "
834 "buffer too small\n");
839 assert(newlinelen
< linesize
);
840 assert(sd
[newlinelen
] == '\0');
841 assert(sd
[newlinelen
-1] == ']');
842 assert(sd
[newlinelen
-2] == '"');
844 buffer
->sd
= strdup(sd
);
853 sign_string_sign(char *line
, char **signature
)
855 char buf
[SIGN_MAX_LENGTH
+1];
856 unsigned char sig_value
[SIGN_B64SIGLEN_DSS
];
857 unsigned char sig_b64
[SIGN_B64SIGLEN_DSS
];
858 unsigned sig_len
= 0;
861 * The signature is calculated over the completely formatted
862 * syslog-message, including all of the PRI, HEADER, and hashes
863 * in the hash block, excluding spaces between fields, and also
864 * excluding the signature field (SD Parameter Name "SIGN", "=",
865 * and corresponding value).
867 * -- I am not quite sure which spaces are to be removed.
868 * Only the ones inside the "ssign" element or those between
869 * header fields as well?
871 /* removes the string ' SIGN=""' */
872 for (p
= line
, q
= buf
;
873 *p
&& (q
- buf
<= SIGN_MAX_LENGTH
);) {
874 if (strncmp(p
, " SIGN=\"\"", 8) == 0)
880 SSL_CHECK_ONE(EVP_SignInit(GlobalSign
.sigctx
, GlobalSign
.sig
));
881 SSL_CHECK_ONE(EVP_SignUpdate(GlobalSign
.sigctx
, buf
, q
-buf
));
882 assert(GlobalSign
.privkey
);
883 SSL_CHECK_ONE(EVP_SignFinal(GlobalSign
.sigctx
, sig_value
, &sig_len
,
884 GlobalSign
.privkey
));
886 b64_ntop(sig_value
, sig_len
, (char *)sig_b64
, sizeof(sig_b64
));
887 *signature
= strdup((char *)sig_b64
);
889 DPRINTF((D_CALL
|D_SIGN
), "sign_string_sign('%s') --> '%s'\n",
891 return *signature
!= NULL
;
895 sign_new_reboot_session()
897 struct signature_group_t
*sg
;
899 DPRINTF((D_CALL
|D_SIGN
), "sign_new_reboot_session()\n");
901 /* global counters */
903 /* might be useful for later analysis:
904 * rebooted session IDs are sequential,
905 * normal IDs are almost always not */
908 assert(GlobalSign
.sg
<= 3);
910 STAILQ_FOREACH(sg
, &GlobalSign
.SigGroups
, entries
) {
911 sg
->resendcount
= SIGN_RESENDCOUNT_CERTBLOCK
;
912 sg
->last_msg_num
= 1;
916 /* get msg_num, increment counter, check overflow */
918 sign_assign_msg_num(struct signature_group_t
*sg
)
922 old
= sg
->last_msg_num
++;
923 if (sg
->last_msg_num
> SIGN_MAX_COUNT
)
924 sign_new_reboot_session();
929 /* increment gbc, check overflow */
933 if (++GlobalSign
.gbc
> SIGN_MAX_COUNT
)
934 sign_new_reboot_session();
936 #endif /* !DISABLE_SIGN */