Added challenge-response authentication to rrdcached. ---
Sending this along RFC while I finish up the client code.. -kbrint --- diff --git a/doc/rrdcached.pod b/doc/rrdcached.pod index 5113c2d..20b2cf7 100644 --- a/doc/rrdcached.pod +++ b/doc/rrdcached.pod @@ -8,6 +8,7 @@ rrdcached - Data caching daemon for rrdtool B<rrdcached> [B<-l/-L>E<nbsp>I<address>] +[B<-A>E<nbsp>I<auth_file>] [B<-w>E<nbsp>I<timeout>] [B<-z>E<nbsp>I<delay>] [B<-f>E<nbsp>I<timeout>] @@ -67,6 +68,13 @@ C<unix:/tmp/rrdcached.sock>, will be used. Same as B<-l>, except creates a low-privilege socket. See B<SECURITY CONSIDERATIONS> for more information. +=item B<-A> I<auth_file> + +This file contains a list of authentication secrets, one per line. +Comments start with B<#>; any remaining characters on the line are +ignored. Trailing white space is ignored. See B<AUTHENTICATION> for more +information. + =item B<-w> I<timeout> Data is written to disk every I<timeout> seconds. If this option is not @@ -305,14 +313,13 @@ ASCII art rocks. =head1 SECURITY CONSIDERATIONS -The client/server protocol does not have any authentication or -authorization mechanism. Therefore, take care to restrict which users can -connect to the daemon. - Control sockets are divided into high-privilege (B<-l>) and low-privilege (B<-L>) sockets. High-privilege sockets accept all commands, whereas low-privilege sockets accept only B<FLUSH>, B<STATS>, and B<HELP>. +A user connecting on a low-privilege socket may upgrade to high privilege +upon successful authentication. See L<AUTHENTICATION> below. + For a multi-user environment where only certain users require read/write access, the recommended configuration uses two sockets as follows: @@ -493,6 +500,58 @@ Disconnect from rrdcached. =back +=head2 AUTHENTICATION + +B<rrdcached> employs a shared secret, challenge-response mechanism. The +secrets are known to both rrdcached and the client, though they are not +transmitted "on the wire". The protocol is as follows: + +=over + +=item B<Successful authentication> + + client: AUTHSTART + server: 1 challenge follows + server: <nonce> + client: AUTH <response> + server: 0 ok + +=item B<Unsuccessful authentication> + + client: AUTHSTART + server: 1 Challenge follows. + server: <nonce> + client: AUTH <bad_response> + server: -1 bad authentication response + +=item B<No authentication required> + + client: AUTHSTART + server: 0 Authentication not required. + +=back + +The authentication variables are defined as follows: + +=over + +=item B<E<lt>nonceE<gt>> + +This string of random characters is specified by the server. Currently, +the nonce is 40 printable characters. The client must accept any string +of length E<gt>= 1. + +=item B<E<lt>reponseE<gt>> + +The response is calculated by the user. The correct response is: + + HMAC_SHA1(<nonce>, <key>) + +The response must be expressed in 40 characters of hexidecimal +(i.e. "af09deadbeef..."). The response is not case-sensitive. + +=back + =head2 Performance Values The following counters are returned by the B<STATS> command: diff --git a/src/Makefile.am b/src/Makefile.am index 129e3ad..2a59612 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -16,6 +16,8 @@ AM_CFLAGS = @CFLAGS@ ## no including this by default @WERROR@ UPD_C_FILES = \ + rrd_auth.c \ + sha1.c \ rrd_parsetime.c \ rrd_hw.c \ rrd_hw_math.c \ @@ -51,7 +53,7 @@ RRD_C_FILES = \ rrd_tune.c noinst_HEADERS = \ - unused.h \ + unused.h sha1.h \ rrd_getopt.h rrd_parsetime.h \ rrd_config_bottom.h rrd_i18n.h \ rrd_format.h rrd_tool.h rrd_xport.h rrd.h rrd_rpncalc.h \ diff --git a/src/librrd.sym.in.in b/src/librrd.sym.in.in index f947fe5..c5caaf2 100644 --- a/src/librrd.sym.in.in +++ b/src/librrd.sym.in.in @@ -1,5 +1,7 @@ rrd_add_ptr rrd_add_strdup +rrd_auth_response +rrd_auth_loadfile rrd_clear_error rrd_close rrd_cmd_flush diff --git a/src/rrd_auth.c b/src/rrd_auth.c new file mode 100644 index 0000000..6fe412c --- /dev/null +++ b/src/rrd_auth.c @@ -0,0 +1,74 @@ +/** + * RRDTool - src/rrd_auth.c + * Authentication for client/server authentication. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; only version 2 of the License is applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + **/ + +#include "rrd_tool.h" +#include "rrd_client.h" +#include "sha1.h" + +#include <string.h> + +/* calculate the hex string for the (nonce,secret) pair. store + * in the dest buffer, which must be at least RRD_AUTH_RESPONSE_LEN in size. + */ +void rrd_auth_response(char *dest, char *nonce, char *secret) +{ + sha1_context ctx; + unsigned char result[20]; + unsigned char *res_p; + int i; + static char hex[] = "0123456789abcdef"; + + sha1_hmac_starts(&ctx, (unsigned char *)nonce, strlen(nonce)); + sha1_hmac_update(&ctx, (unsigned char *)secret, strlen(secret)); + sha1_hmac_finish(&ctx, result); + + for (i=0, res_p = result; i<20; i++, res_p++) + { + *(dest++) = hex[ *res_p >> 4 ]; + *(dest++) = hex[ *res_p & 0xf ]; + } +} + +/* load secrets from the specified file. + * returns 0 on success, otherwise error code suitable for strerror() + */ +int rrd_auth_loadfile(char *file, char ***secrets, size_t *cnt) +{ + FILE *fh; + char line[RRD_AUTH_SECRET_MAX]; + char *eol; + + if ((fh = fopen(file, "r")) == NULL) + return errno; + + while (fgets(line, sizeof(line), fh)) { + eol = line; + strsep(&eol, "#\n"); + + /* trim out trailing white space */ + eol -= 2; + while (eol > line && isspace((int) *eol)) + *(eol--) = '\0'; + + if (line[0]) + rrd_add_strdup(secrets, cnt, line); + } + + fclose(fh); + return (0); +} diff --git a/src/rrd_client.h b/src/rrd_client.h index 90b78f8..4ad168b 100644 --- a/src/rrd_client.h +++ b/src/rrd_client.h @@ -44,6 +44,11 @@ #define RRDCACHED_DEFAULT_PORT "42217" #define ENV_RRDCACHED_ADDRESS "RRDCACHED_ADDRESS" +#define RRD_AUTH_SECRET_MAX 4096 +/* length of hex SHA1 response */ +#define RRD_AUTH_RESPONSE_LEN (20*2 + 1) +void rrd_auth_response(char *dest, char *nonce, char *secret); +int rrd_auth_loadfile(char *file, char ***secrets, size_t *cnt); // Windows version has no daemon/client support diff --git a/src/rrd_daemon.c b/src/rrd_daemon.c index 53904a3..6443240 100644 --- a/src/rrd_daemon.c +++ b/src/rrd_daemon.c @@ -129,6 +129,7 @@ struct listen_socket_s char addr[PATH_MAX + 1]; int family; socket_privilege privilege; + char *auth_nonce; /* state for BATCH processing */ time_t batch_start; @@ -243,6 +244,9 @@ static char *config_base_dir = NULL; static size_t _config_base_dir_len = 0; static int config_write_base_only = 0; +static char **auth_secrets = NULL; +static size_t auth_secrets_cnt = 0; + static listen_socket_t **config_listen_address_list = NULL; static size_t config_listen_address_list_len = 0; @@ -1472,6 +1476,92 @@ static int handle_request_quit (HANDLER_PROTO) /* {{{ */ return -1; } /* }}} static int handle_request_quit */ +#define NONCE_SIZE 40 +static char *NONCE_CHARS = + "()*+,-./0123456789<=>?@" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ_" + "abcdefghijklmnopqrstuvwxyz~"; + +static char *auth_nonce(void) /* {{{ */ +{ + char *n, *np; + int i, nchars; + + np = n = malloc(NONCE_SIZE+1); + if (n == NULL) + return NULL; + + nchars = strlen(NONCE_CHARS); + + for (i=0; i < NONCE_SIZE; i++, np++) + *np = NONCE_CHARS[ rrd_random() % nchars ]; + + *np = '\0'; + return n; +} /* }}} static char *auth_nonce */ + +static int can_auth (listen_socket_t *sock) /* {{{ */ +{ + if (!auth_secrets) + return send_response(sock, RESP_OK, "auth not enabled\n"); + + if (sock->privilege >= PRIV_HIGH) + return send_response(sock, RESP_OK, "auth not necessary\n"); + + return 1; +} /* }}} static int can_auth() */ + +static int handle_request_authstart (HANDLER_PROTO) /* {{{ */ +{ + int status; + + status = can_auth(sock); + if (status <= 0) + return status; + + if (!sock->auth_nonce) + sock->auth_nonce = auth_nonce(); + + if (sock->auth_nonce) + { + add_response_info(sock, "%s\n", sock->auth_nonce); + return send_response(sock, RESP_OK, "challenge follows\n"); + } + else + return send_response(sock, RESP_OK, "Authentication not supported\n"); +} /* }}} static int handle_reqeust_authstart */ + +static int handle_request_auth (HANDLER_PROTO) /* {{{ */ +{ + char *response; + int status; + + status = can_auth(sock); + if (status <= 0) + return status; + + if (!sock->auth_nonce) + return send_response(sock, RESP_ERR, "Try 'AUTHSTART' first.\n"); + + status = buffer_get_field (&buffer, &buffer_size, &response); + if (status != 0) + return syntax_error(sock,cmd); + + for (size_t i=0; i < auth_secrets_cnt; i++) + { + char expected[RRD_AUTH_RESPONSE_LEN]; + rrd_auth_response(expected, sock->auth_nonce, auth_secrets[i]); + + if (strcasecmp(expected, response) == 0) + { + sock->privilege = PRIV_HIGH; + return send_response(sock, RESP_OK, "ok\n"); + } + } + + return send_response(sock, RESP_ERR, "bad authentication response\n"); +} /* }}} */ + struct command COMMANDS[] = { { "UPDATE", @@ -1599,6 +1689,27 @@ struct command COMMANDS[] = { NULL }, { + "AUTHSTART", + handle_request_authstart, + PRIV_LOW, + CMD_CONTEXT_CLIENT, + "AUTHSTART\n" + , + "Begin challenge/response authentication. The client must\n" + "return HMAC_SHA1(challenge,secret) with the 'AUTH' command.\n" + "See rrdcached(1) for more information.\n" + }, + { + "AUTH", + handle_request_auth, + PRIV_LOW, + CMD_CONTEXT_CLIENT, + "AUTH <response>\n" + , + "Authenticate to the server. Must have already issued 'AUTHSTART'.\n" + "See rrdcached(1) for more information.\n" + }, + { "QUIT", handle_request_quit, PRIV_LOW, @@ -1942,6 +2053,7 @@ static void free_listen_socket(listen_socket_t *sock) /* {{{ */ free(sock->rbuf); sock->rbuf = NULL; free(sock->wbuf); sock->wbuf = NULL; + free(sock->auth_nonce); sock->auth_nonce = NULL; free(sock); } /* }}} void free_listen_socket */ @@ -2506,6 +2618,7 @@ static int cleanup (void) /* {{{ */ free(config_pid_file); free(journal_cur); free(journal_old); + rrd_free_ptrs((void ***) &auth_secrets, &auth_secrets_cnt); pthread_mutex_lock(&cache_lock); g_tree_destroy(cache_tree); @@ -2521,7 +2634,7 @@ static int read_options (int argc, char **argv) /* {{{ */ int option; int status = 0; - while ((option = getopt(argc, argv, "gl:L:f:w:z:t:Bb:p:Fj:h?")) != -1) + while ((option = getopt(argc, argv, "gl:L:A:f:w:z:t:Bb:p:Fj:h?")) != -1) { switch (option) { @@ -2554,6 +2667,22 @@ static int read_options (int argc, char **argv) /* {{{ */ } break; + case 'A': + { + status = rrd_auth_loadfile(optarg, &auth_secrets, &auth_secrets_cnt); + if (status) + { + fprintf(stderr, "Cannot read authentication file: %s : %s\n", + optarg, rrd_strerror(status)); + return status; + } + + if (auth_secrets_cnt < 1) + fprintf(stderr, "WARNING: -A '%s': file contains no auth secrets\n", + optarg); + } + break; + case 'f': { int temp; diff --git a/src/sha1.c b/src/sha1.c new file mode 100644 index 0000000..33aa0a9 --- /dev/null +++ b/src/sha1.c @@ -0,0 +1,397 @@ +/** + * RRDTool src/sha1.c + * + * This file was adapted from PolarSSL. Unnecessary code has been removed. + * Original license follows. + * + ***************************************************************** + * + * FIPS-180-1 compliant SHA-1 implementation + * + * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine + * + * Copyright (C) 2009 Paul Bakker <polarssl_maintainer at polarssl dot org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * The SHA-1 standard was published by NIST in 1993. + * + * http://www.itl.nist.gov/fipspubs/fip180-1.htm + */ + +#include "sha1.h" + +#include <string.h> +#include <stdio.h> + +/* + * 32-bit integer manipulation macros (big endian) + */ +#ifndef GET_ULONG_BE +#define GET_ULONG_BE(n,b,i) \ +{ \ + (n) = ( (unsigned long) (b)[(i) ] << 24 ) \ + | ( (unsigned long) (b)[(i) + 1] << 16 ) \ + | ( (unsigned long) (b)[(i) + 2] << 8 ) \ + | ( (unsigned long) (b)[(i) + 3] ); \ +} +#endif + +#ifndef PUT_ULONG_BE +#define PUT_ULONG_BE(n,b,i) \ +{ \ + (b)[(i) ] = (unsigned char) ( (n) >> 24 ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) ); \ +} +#endif + +/* + * SHA-1 context setup + */ +void sha1_starts( sha1_context *ctx ) +{ + ctx->total[0] = 0; + ctx->total[1] = 0; + + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; + ctx->state[4] = 0xC3D2E1F0; +} + +static void sha1_process( sha1_context *ctx, unsigned char data[64] ) +{ + unsigned long temp, W[16], A, B, C, D, E; + + GET_ULONG_BE( W[ 0], data, 0 ); + GET_ULONG_BE( W[ 1], data, 4 ); + GET_ULONG_BE( W[ 2], data, 8 ); + GET_ULONG_BE( W[ 3], data, 12 ); + GET_ULONG_BE( W[ 4], data, 16 ); + GET_ULONG_BE( W[ 5], data, 20 ); + GET_ULONG_BE( W[ 6], data, 24 ); + GET_ULONG_BE( W[ 7], data, 28 ); + GET_ULONG_BE( W[ 8], data, 32 ); + GET_ULONG_BE( W[ 9], data, 36 ); + GET_ULONG_BE( W[10], data, 40 ); + GET_ULONG_BE( W[11], data, 44 ); + GET_ULONG_BE( W[12], data, 48 ); + GET_ULONG_BE( W[13], data, 52 ); + GET_ULONG_BE( W[14], data, 56 ); + GET_ULONG_BE( W[15], data, 60 ); + +#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) + +#define R(t) \ +( \ + temp = W[(t - 3) & 0x0F] ^ W[(t - 8) & 0x0F] ^ \ + W[(t - 14) & 0x0F] ^ W[ t & 0x0F], \ + ( W[t & 0x0F] = S(temp,1) ) \ +) + +#define P(a,b,c,d,e,x) \ +{ \ + e += S(a,5) + F(b,c,d) + K + x; b = S(b,30); \ +} + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + E = ctx->state[4]; + +#define F(x,y,z) (z ^ (x & (y ^ z))) +#define K 0x5A827999 + + P( A, B, C, D, E, W[0] ); + P( E, A, B, C, D, W[1] ); + P( D, E, A, B, C, W[2] ); + P( C, D, E, A, B, W[3] ); + P( B, C, D, E, A, W[4] ); + P( A, B, C, D, E, W[5] ); + P( E, A, B, C, D, W[6] ); + P( D, E, A, B, C, W[7] ); + P( C, D, E, A, B, W[8] ); + P( B, C, D, E, A, W[9] ); + P( A, B, C, D, E, W[10] ); + P( E, A, B, C, D, W[11] ); + P( D, E, A, B, C, W[12] ); + P( C, D, E, A, B, W[13] ); + P( B, C, D, E, A, W[14] ); + P( A, B, C, D, E, W[15] ); + P( E, A, B, C, D, R(16) ); + P( D, E, A, B, C, R(17) ); + P( C, D, E, A, B, R(18) ); + P( B, C, D, E, A, R(19) ); + +#undef K +#undef F + +#define F(x,y,z) (x ^ y ^ z) +#define K 0x6ED9EBA1 + + P( A, B, C, D, E, R(20) ); + P( E, A, B, C, D, R(21) ); + P( D, E, A, B, C, R(22) ); + P( C, D, E, A, B, R(23) ); + P( B, C, D, E, A, R(24) ); + P( A, B, C, D, E, R(25) ); + P( E, A, B, C, D, R(26) ); + P( D, E, A, B, C, R(27) ); + P( C, D, E, A, B, R(28) ); + P( B, C, D, E, A, R(29) ); + P( A, B, C, D, E, R(30) ); + P( E, A, B, C, D, R(31) ); + P( D, E, A, B, C, R(32) ); + P( C, D, E, A, B, R(33) ); + P( B, C, D, E, A, R(34) ); + P( A, B, C, D, E, R(35) ); + P( E, A, B, C, D, R(36) ); + P( D, E, A, B, C, R(37) ); + P( C, D, E, A, B, R(38) ); + P( B, C, D, E, A, R(39) ); + +#undef K +#undef F + +#define F(x,y,z) ((x & y) | (z & (x | y))) +#define K 0x8F1BBCDC + + P( A, B, C, D, E, R(40) ); + P( E, A, B, C, D, R(41) ); + P( D, E, A, B, C, R(42) ); + P( C, D, E, A, B, R(43) ); + P( B, C, D, E, A, R(44) ); + P( A, B, C, D, E, R(45) ); + P( E, A, B, C, D, R(46) ); + P( D, E, A, B, C, R(47) ); + P( C, D, E, A, B, R(48) ); + P( B, C, D, E, A, R(49) ); + P( A, B, C, D, E, R(50) ); + P( E, A, B, C, D, R(51) ); + P( D, E, A, B, C, R(52) ); + P( C, D, E, A, B, R(53) ); + P( B, C, D, E, A, R(54) ); + P( A, B, C, D, E, R(55) ); + P( E, A, B, C, D, R(56) ); + P( D, E, A, B, C, R(57) ); + P( C, D, E, A, B, R(58) ); + P( B, C, D, E, A, R(59) ); + +#undef K +#undef F + +#define F(x,y,z) (x ^ y ^ z) +#define K 0xCA62C1D6 + + P( A, B, C, D, E, R(60) ); + P( E, A, B, C, D, R(61) ); + P( D, E, A, B, C, R(62) ); + P( C, D, E, A, B, R(63) ); + P( B, C, D, E, A, R(64) ); + P( A, B, C, D, E, R(65) ); + P( E, A, B, C, D, R(66) ); + P( D, E, A, B, C, R(67) ); + P( C, D, E, A, B, R(68) ); + P( B, C, D, E, A, R(69) ); + P( A, B, C, D, E, R(70) ); + P( E, A, B, C, D, R(71) ); + P( D, E, A, B, C, R(72) ); + P( C, D, E, A, B, R(73) ); + P( B, C, D, E, A, R(74) ); + P( A, B, C, D, E, R(75) ); + P( E, A, B, C, D, R(76) ); + P( D, E, A, B, C, R(77) ); + P( C, D, E, A, B, R(78) ); + P( B, C, D, E, A, R(79) ); + +#undef K +#undef F + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; + ctx->state[4] += E; +} + +/* + * SHA-1 process buffer + */ +void sha1_update( sha1_context *ctx, unsigned char *input, int ilen ) +{ + int fill; + unsigned long left; + + if( ilen <= 0 ) + return; + + left = ctx->total[0] & 0x3F; + fill = 64 - left; + + ctx->total[0] += ilen; + ctx->total[0] &= 0xFFFFFFFF; + + if( ctx->total[0] < (unsigned long) ilen ) + ctx->total[1]++; + + if( left && ilen >= fill ) + { + memcpy( (void *) (ctx->buffer + left), + (void *) input, fill ); + sha1_process( ctx, ctx->buffer ); + input += fill; + ilen -= fill; + left = 0; + } + + while( ilen >= 64 ) + { + sha1_process( ctx, input ); + input += 64; + ilen -= 64; + } + + if( ilen > 0 ) + { + memcpy( (void *) (ctx->buffer + left), + (void *) input, ilen ); + } +} + +static const unsigned char sha1_padding[64] = +{ + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* + * SHA-1 final digest + */ +void sha1_finish( sha1_context *ctx, unsigned char output[20] ) +{ + unsigned long last, padn; + unsigned long high, low; + unsigned char msglen[8]; + + high = ( ctx->total[0] >> 29 ) + | ( ctx->total[1] << 3 ); + low = ( ctx->total[0] << 3 ); + + PUT_ULONG_BE( high, msglen, 0 ); + PUT_ULONG_BE( low, msglen, 4 ); + + last = ctx->total[0] & 0x3F; + padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); + + sha1_update( ctx, (unsigned char *) sha1_padding, padn ); + sha1_update( ctx, msglen, 8 ); + + PUT_ULONG_BE( ctx->state[0], output, 0 ); + PUT_ULONG_BE( ctx->state[1], output, 4 ); + PUT_ULONG_BE( ctx->state[2], output, 8 ); + PUT_ULONG_BE( ctx->state[3], output, 12 ); + PUT_ULONG_BE( ctx->state[4], output, 16 ); +} + +/* + * output = SHA-1( input buffer ) + */ +void sha1( unsigned char *input, int ilen, unsigned char output[20] ) +{ + sha1_context ctx; + + sha1_starts( &ctx ); + sha1_update( &ctx, input, ilen ); + sha1_finish( &ctx, output ); + + memset( &ctx, 0, sizeof( sha1_context ) ); +} + +/* + * SHA-1 HMAC context setup + */ +void sha1_hmac_starts( sha1_context *ctx, unsigned char *key, int keylen ) +{ + int i; + unsigned char sum[20]; + + if( keylen > 64 ) + { + sha1( key, keylen, sum ); + keylen = 20; + key = sum; + } + + memset( ctx->ipad, 0x36, 64 ); + memset( ctx->opad, 0x5C, 64 ); + + for( i = 0; i < keylen; i++ ) + { + ctx->ipad[i] = (unsigned char)( ctx->ipad[i] ^ key[i] ); + ctx->opad[i] = (unsigned char)( ctx->opad[i] ^ key[i] ); + } + + sha1_starts( ctx ); + sha1_update( ctx, ctx->ipad, 64 ); + + memset( sum, 0, sizeof( sum ) ); +} + +/* + * SHA-1 HMAC process buffer + */ +void sha1_hmac_update( sha1_context *ctx, unsigned char *input, int ilen ) +{ + sha1_update( ctx, input, ilen ); +} + +/* + * SHA-1 HMAC final digest + */ +void sha1_hmac_finish( sha1_context *ctx, unsigned char output[20] ) +{ + unsigned char tmpbuf[20]; + + sha1_finish( ctx, tmpbuf ); + sha1_starts( ctx ); + sha1_update( ctx, ctx->opad, 64 ); + sha1_update( ctx, tmpbuf, 20 ); + sha1_finish( ctx, output ); + + memset( tmpbuf, 0, sizeof( tmpbuf ) ); +} + +/* + * output = HMAC-SHA-1( hmac key, input buffer ) + */ +void sha1_hmac( unsigned char *key, int keylen, + unsigned char *input, int ilen, + unsigned char output[20] ) +{ + sha1_context ctx; + + sha1_hmac_starts( &ctx, key, keylen ); + sha1_hmac_update( &ctx, input, ilen ); + sha1_hmac_finish( &ctx, output ); + + memset( &ctx, 0, sizeof( sha1_context ) ); +} diff --git a/src/sha1.h b/src/sha1.h new file mode 100644 index 0000000..298790f --- /dev/null +++ b/src/sha1.h @@ -0,0 +1,125 @@ +/** + * RRDTool src/sha1.c + * + * This file was adapted from PolarSSL. Unnecessary code has been removed. + * Original license follows. + * + ***************************************************************** + * \file sha1.h + * + * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine + * + * Copyright (C) 2009 Paul Bakker <polarssl_maintainer at polarssl dot org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_SHA1_H +#define POLARSSL_SHA1_H + +/** + * \brief SHA-1 context structure + */ +typedef struct +{ + unsigned long total[2]; /*!< number of bytes processed */ + unsigned long state[5]; /*!< intermediate digest state */ + unsigned char buffer[64]; /*!< data block being processed */ + + unsigned char ipad[64]; /*!< HMAC: inner padding */ + unsigned char opad[64]; /*!< HMAC: outer padding */ +} +sha1_context; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief SHA-1 context setup + * + * \param ctx context to be initialized + */ +void sha1_starts( sha1_context *ctx ); + +/** + * \brief SHA-1 process buffer + * + * \param ctx SHA-1 context + * \param input buffer holding the data + * \param ilen length of the input data + */ +void sha1_update( sha1_context *ctx, unsigned char *input, int ilen ); + +/** + * \brief SHA-1 final digest + * + * \param ctx SHA-1 context + * \param output SHA-1 checksum result + */ +void sha1_finish( sha1_context *ctx, unsigned char output[20] ); + +/** + * \brief Output = SHA-1( input buffer ) + * + * \param input buffer holding the data + * \param ilen length of the input data + * \param output SHA-1 checksum result + */ +void sha1( unsigned char *input, int ilen, unsigned char output[20] ); + +/** + * \brief SHA-1 HMAC context setup + * + * \param ctx HMAC context to be initialized + * \param key HMAC secret key + * \param keylen length of the HMAC key + */ +void sha1_hmac_starts( sha1_context *ctx, unsigned char *key, int keylen ); + +/** + * \brief SHA-1 HMAC process buffer + * + * \param ctx HMAC context + * \param input buffer holding the data + * \param ilen length of the input data + */ +void sha1_hmac_update( sha1_context *ctx, unsigned char *input, int ilen ); + +/** + * \brief SHA-1 HMAC final digest + * + * \param ctx HMAC context + * \param output SHA-1 HMAC checksum result + */ +void sha1_hmac_finish( sha1_context *ctx, unsigned char output[20] ); + +/** + * \brief Output = HMAC-SHA-1( hmac key, input buffer ) + * + * \param key HMAC secret key + * \param keylen length of the HMAC key + * \param input buffer holding the data + * \param ilen length of the input data + * \param output HMAC-SHA-1 result + */ +void sha1_hmac( unsigned char *key, int keylen, + unsigned char *input, int ilen, + unsigned char output[20] ); + +#ifdef __cplusplus +} +#endif + +#endif /* sha1.h */ _______________________________________________ rrd-developers mailing list rrd-developers@lists.oetiker.ch https://lists.oetiker.ch/cgi-bin/listinfo/rrd-developers