[rrd-developers] [PATCH] rrdcached server-side authentication

kevin brintnall kbrint at rufus.net
Wed Apr 29 05:14:18 CEST 2009


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 */



More information about the rrd-developers mailing list