]> err.no Git - yubikey-server-c/commitdiff
Various cleanups
authorTollef Fog Heen <tfheen@err.no>
Mon, 12 Oct 2009 06:04:49 +0000 (08:04 +0200)
committerTollef Fog Heen <tfheen@err.no>
Mon, 12 Oct 2009 06:04:49 +0000 (08:04 +0200)
configure.ac
schema.sql
src/Makefile.am
src/main.c

index e7fd913d3768cf8849c86f3aee2181f1ca8f2b84..068003ee2c9ffba5ba05e74307c58f1fc5bd1b07 100644 (file)
@@ -4,7 +4,7 @@ AC_INIT([yubikey-server-c], [0.1],
   )
 AC_CONFIG_AUX_DIR([build-aux])
 AC_CONFIG_MACRO_DIR([m4])
-AM_INIT_AUTOMAKE([-Wall -Werror foreign])
+AM_INIT_AUTOMAKE([-Wall -Wextra -Werror foreign])
 AC_PROG_CC
 
 AC_MSG_CHECKING([for PostgreSQL libraries])
index b42fdc262b3c241dba684f488299ba2e4e63c9b3..de1ef8dab6871cc41ff8191e6b8d8ca3149d2d70 100644 (file)
@@ -6,7 +6,7 @@
 CREATE TABLE yubikey (
        yubikey_id serial NOT NULL,
        active boolean NOT NULL DEFAULT 'f',
-       public_id bytea, -- fixed public bit of the key's output
+       public_id varchar, -- fixed public bit of the key's output, modhex encoded
        secret_uid bytea NOT NULL, -- secret uid bit of the key
        secret_key bytea NOT NULL, 
        session_counter int,
@@ -15,7 +15,7 @@ CREATE TABLE yubikey (
 
 INSERT INTO yubikey (active, public_id,
        secret_uid, secret_key, session_counter, session_use) VALUES 
-       ('t', decode('d4633b', 'hex')
+       ('t', 'tfheen'
        E'\\000\\000\\000\\000\\000\\000',
        decode('baef43c254e9d2217912e80ed71a7b4a', 'hex'),
        0, 0);
index f698982cff0b4e896841e4561b27ca9f1e524127..b9797821337009b6e010aae2c9fa391e80086d02 100644 (file)
@@ -1,7 +1,7 @@
 
 sbin_PROGRAMS = yubikeyd
 
-yubikeyd_SOURCES = main.c
+yubikeyd_SOURCES = main.c util.c
 
 yubikeyd_CFLAGS = @glib_CFLAGS@ @libmicrohttpd_CFLAGS@ @POSTGRESQL_CFLAGS@
 yubikeyd_LDADD = @glib_LIBS@ @libmicrohttpd_LIBS@ @POSTGRESQL_LDFLAGS@
\ No newline at end of file
index a4811cd1c4178cb504c4d7ddf679d6b8dd6f5aa5..f875146f05c61eb182c1d5b3f5be0ff2e021429b 100644 (file)
@@ -18,6 +18,7 @@
 
 #define PORT 8000
 
+#define _GNU_SOURCE
 #include <assert.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <time.h>
 #include <glib.h>
 #include <gcrypt.h>
+#include "util.h"
+#include <arpa/inet.h>
+
+#ifdef UNUSED
+#elif defined(__GNUC__)
+# define UNUSED(x) UNUSED_ ## x __attribute__((unused))
+#elif defined(__LCLINT__)
+# define UNUSED(x) /*@unused@*/ x
+#else
+# define UNUSED(x) x
+#endif
 
 PGconn     *db_conn;
 
@@ -50,23 +62,26 @@ struct error {
        const char *info;
 };
 
+struct ykc_stats {
+       int active;
+       char *public_id;
+       char *secret_uid;
+       char *secret_key;
+       int session_counter;
+       int session_use;
+};
+
 char *get_timestamp(void) {
-       struct tm tm;
        size_t len = strlen("YYYY-mm-ddTHH:MM:SSZMSMS");
        char *ts = malloc(len + 1);
-       char ms[5];
-       struct timeval tv;
-       gettimeofday(&tv, NULL);
-       gmtime_r(&tv.tv_sec, &tm);
-       strftime(ts, len, "%FT%TZ", &tm);
-       sprintf(ms, "%.4d", tv.tv_usec / 1000);
-       strncat(ts, ms, 4);
+
+       ysc_strftime(ts, len + 1, "%FT%TZ%v", NULL);
        return ts;
 }
 
 gchar *sign_request(char *key, size_t key_len, char *info, char *status, 
                   char *timestamp) {
-       char *line, *sig;
+       char *line;
        gchar *ret;
        gcry_md_hd_t hd;
        if (info != NULL) {
@@ -85,43 +100,54 @@ gchar *sign_request(char *key, size_t key_len, char *info, char *status,
        return ret;
 }
 
-static int handle_request(void *data,
-                         struct MHD_Connection *conn,
-                         const char *url,
-                         const char *method,
-                         const char *version,
-                         const char *upload_data,
-                         size_t *upload_data_size,
-                         void **con_cls)
+static int send_response(struct MHD_Connection *conn,
+                        const char *signature,
+                        const char *status,
+                        const char *info,
+                        const char *timestamp)
 {
-       static char *ret = "This is a test";
+       char *resp_text, *t;
        int r;
+       size_t r_l;
        struct MHD_Response *response;
-       PGresult   *res;
-       const char *paramValues[3];
-       int paramLengths[3];
-       int paramFormats[3];
-       int i;
-       const unsigned char *id = NULL, *otp_mh = NULL, *h = NULL, *secret_id;
-       unsigned char *aes_key, *otp, *uid, *timestamp, *signature;
-       unsigned char *shared_secret, *resp_text;
-       size_t shared_secret_len, otp_len, uid_len;
-       struct error *e;
-       yubikey_token_st token;
-       memset(&token, '\0', sizeof(token));
 
-       /* Parse query string, grab id, otp_mh and h (optional) */
+       r_l = strlen("h=\nstatus=\ntimestamp=\ninfo="); /* This is a maximum
+                                               * of static strings */
+       r_l += (signature != NULL ? strlen(signature) : 0);
+       r_l += (status != NULL ? strlen(status) : 0);
+       r_l += (info != NULL ? strlen(info) : 0);
+       r_l += (timestamp != NULL ? strlen(timestamp) : 0);
 
-       id = MHD_lookup_connection_value(conn, MHD_GET_ARGUMENT_KIND, "id");
-       otp_mh = MHD_lookup_connection_value(conn, MHD_GET_ARGUMENT_KIND, "otp");
-       h = MHD_lookup_connection_value(conn, MHD_GET_ARGUMENT_KIND, "h");
-       fprintf(stderr, "got params: url=%s id=%s otp=%s, h=%s\n", url, id, 
-               otp_mh, h);
-       /* XXX Handle missing params here */
+       resp_text = malloc(r_l + 1);
+       t = resp_text;
+       if (signature) {
+               t += sprintf(t, "h=%s\n", signature);
+       }
+       if (status) {
+               t += sprintf(t, "status=%s\n", status);
+       }
+       if (info) {
+               t += sprintf(t, "info=%s\n", info);
+       }
+       if (timestamp) {
+               t += sprintf(t, "timestamp=%s\n", timestamp);
+       }
+       /* XXX error checking above */
 
-       otp_len = strlen(otp_mh)/2 + 1;
-       otp = malloc(otp_len);
-       yubikey_modhex_decode(otp, otp_mh, otp_len);
+       response = MHD_create_response_from_data(strlen(resp_text), resp_text,
+                                                MHD_YES, MHD_YES);
+       r = MHD_queue_response(conn, MHD_HTTP_OK, response);
+       MHD_destroy_response(response);
+       if (r == MHD_YES)
+               return 0;
+       return -1;
+}
+
+static int get_shared_secret(const char *id, char **shared_secret,
+                            size_t *shared_secret_len)
+{
+       const char *paramValues[1];
+       PGresult   *res;
 
        /* Do query to grab shared secret, we need this later anyway */
        paramValues[0] = id;
@@ -137,113 +163,223 @@ static int handle_request(void *data,
        if (PQresultStatus(res) != PGRES_TUPLES_OK) {
                fprintf(stderr, "SELECT failed: %s", PQerrorMessage(db_conn));
                PQclear(res);
-               return MHD_NO;
-               /* XXX Better error handling.*/
-               /* XXX Handle zero rows too */
+               return -1;
+               /* XXX Return error object */
        }
        if (PQntuples(res) == 0) {
                /* XXX Better handling */
                fprintf(stderr, "No such secrets: %s\n", id);
                PQclear(res);
-               return MHD_NO;
+               return -1;
        }
-
-       /* If h exists, verify. FIXME */
-       shared_secret_len = PQgetlength(res, 0, 0);
-       shared_secret = malloc(shared_secret_len);
-       memcpy(shared_secret, PQgetvalue(res, 0, 0), shared_secret_len);
+       *shared_secret_len = PQgetlength(res, 0, 0);
+       *shared_secret = malloc(*shared_secret_len);
+       memcpy(*shared_secret, PQgetvalue(res, 0, 0), *shared_secret_len);
        PQclear(res);
+       return 0;
+}
 
-       /* Validate OTP */
-       /* Find public uid, if possible */
-       if (otp_len > YUBIKEY_BLOCK_SIZE) {
-               uid_len = otp_len - YUBIKEY_BLOCK_SIZE - 1;
-               uid = malloc(uid_len);
-               memcpy(uid, otp, uid_len);
-               /* XXX error checking */
+static int split_otp(const char *otp, char **user, char **s_otp)
+{
+       size_t otp_len, i, j;
+       /* Modhex doubles the length of the string, so grab the last
+        * YUBIKEY_BLOCK_SIZE * 2 octets to get the actual OTP */
+       otp_len = strlen(otp);
+
+       assert(otp_len > YUBIKEY_BLOCK_SIZE * 2);
+       assert(yubikey_modhex_p(otp));
+
+       /* XXX error checking */
+       i = otp_len - YUBIKEY_BLOCK_SIZE * 2;
+       *user = malloc(i+1);
+       memcpy(*user, otp, i);
+       (*user)[i] = '\0';
+
+       j = YUBIKEY_BLOCK_SIZE * 2;
+       *s_otp = malloc(j+1);
+       memcpy(*s_otp, otp + i, j);
+       (*s_otp)[j] = '\0';
+       return 0;
+}
+
+static int set_data_for_uid(char *uid, struct ykc_stats *stats)
+{
+       PGresult *res;
+       const char *paramValues[3];
+       char ctr[10], use[10];
+
+       paramValues[0] = uid;
+
+       snprintf(ctr, sizeof (ctr), "%d", stats->session_counter);
+       snprintf(use, sizeof (use), "%d", stats->session_use);
+
+       paramValues[1] = ctr;
+       paramValues[2] = use;
+       res = PQexecParams(db_conn,
+                          "UPDATE yubikey SET session_counter = $2, "
+                          "session_use = $3 WHERE public_id = $1",
+                          3,       /* number of params */
+                          NULL,    /* let the backend deduce param type */
+                          paramValues,
+                          NULL,
+                          NULL,
+                          1);      /* ask for binary results */
+
+       if (PQresultStatus(res) != PGRES_COMMAND_OK) {
+               fprintf(stderr, "UPDATE failed: %s\n", PQerrorMessage(db_conn));
+               PQclear(res);
+               return -1;
+               /* XXX Better error handling.*/
        }
+       assert(PQntuples(res) == 0);
+       return 0;
+}
+
+static int get_data_for_uid(char *uid, struct ykc_stats *stats)
+{
+       PGresult *res;
+       const char *paramValues[1];
+       char *tmp;
 
        paramValues[0] = uid;
-       paramLengths[0] = uid_len;
-       paramFormats[0] = 1; /* Binary */
        res = PQexecParams(db_conn,
-                          "SELECT secret_uid, secret_key, session_counter, "
-                          "session_use FROM yubikey WHERE public_id = $1::bytea",
+                          "SELECT active, secret_uid, secret_key, "
+                          "session_counter, session_use FROM yubikey "
+                          "WHERE public_id = $1",
                           1,       /* one param */
                           NULL,    /* let the backend deduce param type */
                           paramValues,
-                          paramLengths,
-                          paramFormats,    /* default to all text params */
+                          NULL,
+                          NULL,    /* default to all text params */
                           1);      /* ask for binary results */
 
        if (PQresultStatus(res) != PGRES_TUPLES_OK)
        {
                fprintf(stderr, "SELECT failed: %s", PQerrorMessage(db_conn));
                PQclear(res);
-               return MHD_NO;
+               return -1;
                /* XXX Better error handling*/
        }
        assert(PQgetlength(res, 0, PQfnumber(res, "secret_key")) == YUBIKEY_KEY_SIZE);
-       aes_key = PQgetvalue(res, 0, PQfnumber(res, "secret_key"));
+       stats->secret_key = ysc_memdup(
+               PQgetvalue(res, 0, PQfnumber(res, "secret_key")),
+               PQgetlength(res, 0, PQfnumber(res, "secret_key")));
+
+       stats->secret_uid = ysc_memdup(
+               PQgetvalue(res, 0, PQfnumber(res, "secret_uid")),
+               PQgetlength(res, 0, PQfnumber(res, "secret_uid")));
+
+       tmp = PQgetvalue(res, 0, PQfnumber(res, "session_counter"));
+       stats->session_counter = ntohl(*((uint32_t *) tmp));
+       tmp = PQgetvalue(res, 0, PQfnumber(res, "session_use"));
+       stats->session_use = ntohl(*((uint32_t *) tmp));
+       stats->public_id = NULL;
+       return 0;
+}
+
+static int handle_request(void * UNUSED(data),
+                         struct MHD_Connection *conn,
+                         const char * url,
+                         const char *UNUSED(method),
+                         const char *UNUSED(version),
+                         const char *UNUSED(upload_data),
+                         size_t *UNUSED(upload_data_size),
+                         void **UNUSED(con_cls))
+{
+       const char *id = NULL, *otp = NULL, *h = NULL;
+       char *uid, *otp_token;
+       char *signature = NULL, *status = NULL, *info = NULL, *timestamp = NULL;
+       char *shared_secret;
+       size_t shared_secret_len;
+       yubikey_token_st token;
+       struct ykc_stats stats;
+       memset(&token, '\0', sizeof(token));
+
+       timestamp = get_timestamp();
+       assert(timestamp != NULL);
+       /* Parse query string, grab id, otp and h (optional) */
+
+       id = MHD_lookup_connection_value(conn, MHD_GET_ARGUMENT_KIND, "id");
+       otp = MHD_lookup_connection_value(conn, MHD_GET_ARGUMENT_KIND, "otp");
+       h = MHD_lookup_connection_value(conn, MHD_GET_ARGUMENT_KIND, "h");
+       fprintf(stderr, "got params: url=%s id=%s otp=%s, h=%s\n", url, id,
+               otp, h);
+
+       /* Do query to grab shared secret, we need this later anyway */
+       if (get_shared_secret(id, &shared_secret, &shared_secret_len) < 0) {
+               /* XXX: Something blew up, assume no such ID */
+               status = "NO_SUCH_CLIENT";
+               send_response(conn, NULL, status, NULL, timestamp);
+               /* XXX free memory */
+               return MHD_YES;
+       }
+
+       if (otp == NULL) {
+               info = "otp";
+               signature = sign_request(shared_secret, shared_secret_len,
+                                        info, "MISSING_PARAMETER", timestamp);
+               send_response(conn, signature, status, NULL, timestamp);
+               /* XXX free memory */
+               return MHD_YES;
+       }
+
+       if (! yubikey_modhex_p(otp)) {
+               signature = sign_request(shared_secret, shared_secret_len,
+                                        NULL, "BAD OTP", timestamp);
+               send_response(conn, signature, status, NULL, timestamp);
+               /* XXX free memory */
+               return MHD_YES;
+       }
+
+       /* XXX: If h exists, verify. FIXME */
+
+       /* Validate OTP */
+       /* Find public uid, if possible */
+       split_otp(otp, &uid, &otp_token);
+       get_data_for_uid(uid, &stats);
 
        /* Argh, yubikey_parse takes in one modhex-ed token (but
         * requires us to strip the public id first, and an unencoded aes key*/
-       yubikey_parse(otp_mh+uid_len*2, aes_key, &token);
+       yubikey_parse((uint8_t*)(otp_token), (const uint8_t *)stats.secret_key, &token);
        if (!yubikey_crc_ok_p((void*)&token) ||
-           memcmp(token.uid,
-                  PQgetvalue(res, 0, PQfnumber(res, "secret_uid")),
-                  YUBIKEY_UID_SIZE) != 0) {
+           memcmp(token.uid, stats.secret_uid, YUBIKEY_UID_SIZE) != 0) {
+               signature = sign_request(shared_secret, shared_secret_len,
+                                        NULL, "BAD OTP", timestamp);
+               send_response(conn, signature, status, NULL, timestamp);
+
                /* XXX: FIXME: check counters too */
-               /* XXX FIXME - bad token => reject */
+               /* XXX FIXME free memory */
                fprintf(stderr, "BAD TOKEN\n");
-               return MHD_NO;
+               return MHD_YES;
        }
-       PQclear(res);
-
-       /* Update status, if appropriate */
-
-       asprintf(&(paramValues[0]), "%d", yubikey_counter(token.ctr));
-       paramLengths[0] = 0;
-       paramFormats[0] = 0; /* Text */
-       asprintf(&(paramValues[1]), "%d", yubikey_counter(token.use));
-       paramLengths[1] = 0;
-       paramFormats[1] = 0; /* Text */
-       paramValues[2] = uid;
-       paramLengths[2] = uid_len;
-       paramFormats[2] = 1; /* Binary */
-       res = PQexecParams(db_conn,
-                          "UPDATE yubikey SET session_counter = $1, "
-                          "session_use = $2 WHERE public_id = $3",
-                          3,       /* number of params */
-                          NULL,    /* let the backend deduce param type */
-                          paramValues,
-                          paramLengths,
-                          paramFormats,
-                          1);      /* ask for binary results */
-
-       if (PQresultStatus(res) != PGRES_COMMAND_OK) {
-               fprintf(stderr, "UPDATE failed: %s\n", PQerrorMessage(db_conn));
-               PQclear(res);
-               return MHD_NO;
-               /* XXX Better error handling.*/
+       if (yubikey_counter(token.ctr) < stats.session_counter ||
+           (yubikey_counter(token.ctr) == stats.session_counter &&
+            token.use <= stats.session_use)) {
+               /* Replay */
+               status = "REPLAYED_OTP";
+               signature = sign_request(shared_secret, shared_secret_len,
+                                        NULL, status, timestamp);
+               send_response(conn, signature, status, NULL, timestamp);
+               /* XXX FIXME free memory */
+               fprintf(stderr, "replay\n");
+               return MHD_YES;
        }
-       assert(PQntuples(res) == 0);
 
+       /* Update status, if appropriate */
+       memset(&stats, 0, sizeof(struct ykc_stats));
+       stats.session_counter = yubikey_counter(token.ctr);
+       stats.session_use = token.use;
+       set_data_for_uid(uid, &stats);
        /* Generate response, sign it */
-       timestamp = get_timestamp();
+       fprintf(stderr, "ok request\n");
+       status = "OK";
        signature = sign_request(shared_secret, shared_secret_len,
-                                NULL, "OK", timestamp);
-       asprintf(&resp_text, "h=%s\nstatus=%s\nt=%s\n", signature, "OK",
-                timestamp);
-       response = MHD_create_response_from_data(strlen(resp_text), resp_text,
-                                                MHD_YES, MHD_YES);
-       r = MHD_queue_response(conn, MHD_HTTP_OK, response);
-       MHD_destroy_response(response);
-       fprintf(stderr, "%s %s\n", method, url);
-       return r;
+                                NULL, status, timestamp);
+       send_response(conn, signature, status, NULL, timestamp);
+       return MHD_YES;
 }
 
-int main(int argc, char **argv)
+int main(int UNUSED(argc), char ** UNUSED(argv))
 {
        struct MHD_Daemon *d;