]> mj.ucw.cz Git - subauth.git/blobdiff - server/auth.c
Real cryptography
[subauth.git] / server / auth.c
index 040189820abcf2b21251893804d7af96bf115913..bafcd525a5626869d22ebb29bb7e17107aa1b6ca 100644 (file)
@@ -5,16 +5,22 @@
  */
 
 #include <ucw/lib.h>
+#include <ucw/base64.h>
 #include <ucw/gary.h>
 #include <ucw/stkstring.h>
+#include <ucw/string.h>
+#include <ucw/unaligned.h>
 
 #include <errno.h>
 #include <fcntl.h>
+#include <gcrypt.h>
 #include <time.h>
 #include <unistd.h>
 
 #include "subauthd.h"
 
+struct auth_token *auth_fake_token;
+
 #define HASH_NODE struct auth_user
 #define HASH_PREFIX(x) user_hash_##x
 #define HASH_KEY_ENDSTRING login
@@ -63,6 +69,22 @@ struct auth_acct *auth_find_acct(struct auth_user *au, struct auth_zone *az, boo
   return aa;
 }
 
+struct auth_token *auth_find_token_passwd(struct auth_acct *aa)
+{
+  CLIST_FOR_EACH(struct auth_token *, at, aa->tokens)
+    if (at->type == TOKEN_PASSWORD)
+      return at;
+  return NULL;
+}
+
+struct auth_token *auth_find_token_generated(struct auth_acct *aa, char *ident)
+{
+  CLIST_FOR_EACH(struct auth_token *, at, aa->tokens)
+    if (at->type == TOKEN_GENERATED && !strcmp(at->ident, ident))
+      return at;
+  return NULL;
+}
+
 void auth_delete_user(struct auth_user *au)
 {
   struct auth_acct *aa;
@@ -97,11 +119,111 @@ struct auth_token *auth_create_token(struct auth_acct *aa)
   return at;
 }
 
+static void auth_hash(char *hash_text, struct auth_token *at, const char *passwd)
+{
+  // This is PBKDF2 based on SHA-256-HMAC
+
+  gcry_md_hd_t md;
+  if (gcry_md_open(&md, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC) != GPG_ERR_NO_ERROR)
+    die("gcry_md_open(CGRY_MD_SHA256) failed");
+  if (gcry_md_setkey(md, passwd, strlen(passwd)) != GPG_ERR_NO_ERROR)
+    die("gcry_md_setkey() failed");
+
+  byte current_hash[HASH_BYTES];
+  gcry_md_write(md, at->salt, strlen(at->salt));
+  byte zeroes[4] = { 0, 0, 0, 0 };
+  gcry_md_write(md, zeroes, 4);
+  memcpy(current_hash, gcry_md_read(md, 0), HASH_BYTES);
+
+  byte accumulator[HASH_BYTES];
+  memcpy(accumulator, current_hash, HASH_BYTES);
+
+  for (uint i=1; i < at->iterations; i++)
+    {
+      gcry_md_reset(md);
+      gcry_md_write(md, current_hash, HASH_BYTES);
+      memcpy(current_hash, gcry_md_read(md, 0), HASH_BYTES);
+      for (uint j=0; j<HASH_BYTES; j++)
+       accumulator[j] ^= current_hash[j];
+    }
+
+  gcry_md_close(md);
+
+  uint out_len = base64_encode(hash_text, accumulator, HASH_BYTES);
+  hash_text[out_len] = 0;
+}
+
+static void set_salt(struct auth_token *at)
+{
+  byte salt_bytes[DEFAULT_SALT_BYTES];
+  gcry_randomize(salt_bytes, DEFAULT_SALT_BYTES, GCRY_STRONG_RANDOM);
+
+  char salt_text[2*DEFAULT_SALT_BYTES + 1];
+  uint salt_len = base64_encode(salt_text, salt_bytes, DEFAULT_SALT_BYTES);
+  salt_text[salt_len] = 0;
+
+  at->salt = xstrdup(salt_text);
+  at->iterations = DEFAULT_HASH_ITERATIONS;
+}
+
 void auth_set_token_passwd(struct auth_token *at, const char *passwd)
 {
   at->type = TOKEN_PASSWORD;
-  at->salt = xstrdup("???");
-  at->hash = xstrdup("???");
+  set_salt(at);
+
+  char hash[MAX_TEXT_HASH_SIZE];
+  auth_hash(hash, at, passwd);
+  at->hash = xstrdup(hash);
+}
+
+char *auth_set_token_generated(struct auth_token *at, const char *comment)
+{
+  char ident_text[2*DEFAULT_IDENT_BYTES + 1];
+  do
+    {
+      byte ident_bytes[DEFAULT_IDENT_BYTES];
+      gcry_randomize(ident_bytes, DEFAULT_IDENT_BYTES, GCRY_STRONG_RANDOM);
+      mem_to_hex(ident_text, ident_bytes, DEFAULT_IDENT_BYTES, 0);
+    }
+  while (auth_find_token_generated(at->acct, ident_text));
+
+  at->type = TOKEN_GENERATED;
+  at->ident = xstrdup(ident_text);
+  at->comment = xstrdup(comment);
+  set_salt(at);
+
+  byte token_bytes[DEFAULT_GENERATED_BYTES];
+  gcry_randomize(token_bytes, DEFAULT_GENERATED_BYTES, GCRY_STRONG_RANDOM);
+
+  uint token_len = 2*DEFAULT_IDENT_BYTES + 1 + 2*DEFAULT_GENERATED_BYTES + 1;
+  char *token_text = xmalloc(token_len);
+  memcpy(token_text, ident_text, 2*DEFAULT_IDENT_BYTES);
+  char *token_rhs = token_text + 2*DEFAULT_IDENT_BYTES;
+  *token_rhs++ = '-';
+  uint out_len = base64_encode(token_rhs, token_bytes, DEFAULT_GENERATED_BYTES);
+  if (out_len > 0 && token_rhs[out_len-1] == '=')
+    out_len--;
+  token_rhs[out_len] = 0;
+
+  char hash[MAX_TEXT_HASH_SIZE];
+  auth_hash(hash, at, token_rhs);
+  at->hash = xstrdup(hash);
+
+  return token_text;
+}
+
+bool auth_check_token(struct auth_token *at, const char *passwd)
+{
+  char hash[MAX_TEXT_HASH_SIZE];
+  auth_hash(hash, at, passwd);
+  return !strcmp(at->hash, hash);
+}
+
+static void auth_create_fake_token(void)
+{
+  struct auth_token *at = xmalloc_zero(sizeof(*at));
+  auth_set_token_passwd(at, "TheKeyIsUnderTheFlyingCarpet");
+  auth_fake_token = at;
 }
 
 static void NONRET db_corrupted(const char *reason)
@@ -158,19 +280,24 @@ static void db_parse_user(struct json_node *ju)
              const char *salt = get_string(jt, "s");
              const char *hash = get_string(jt, "h");
              const char *cmt = get_string(jt, "c");
+             const char *ident = get_string(jt, "i");
              if (!salt || !hash)
                db_corrupted("Token must have salt and hash");
 
-             uint lastmod;
-             if (!get_uint(jt, "m", &lastmod))
-               db_corrupted("Token has invalid last modified time");
-
              struct auth_token *at = auth_create_token(aa);
              at->type = type;
              at->salt = xstrdup(salt);
              at->hash = xstrdup(hash);
              at->comment = xstrdup(cmt);
+             at->ident = xstrdup(ident);
+
+             uint lastmod;
+             if (!get_uint(jt, "m", &lastmod))
+               db_corrupted("Token has invalid last modified time");
              at->last_modified = lastmod;
+
+             if (!get_uint(jt, "n", &at->iterations))
+               db_corrupted("Token has invalid number of hash iterations");
            }
        }
     }
@@ -254,6 +381,9 @@ void db_write(void)
                json_object_set(jt, "h", json_new_string_ref(js, at->hash));
              if (at->comment)
                json_object_set(jt, "c", json_new_string_ref(js, at->comment));
+             if (at->ident)
+               json_object_set(jt, "i", json_new_string_ref(js, at->ident));
+             json_object_set(jt, "n", json_new_number(js, at->iterations));
              json_object_set(jt, "m", json_new_number(js, at->last_modified));
            }
          if (GARY_SIZE(jts->elements))
@@ -279,6 +409,12 @@ void db_write(void)
 
 void auth_init(void)
 {
+  if (!gcry_check_version(GCRYPT_VERSION))
+    die("libgcrypt version mismatch");
+  gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
+  gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+
   user_hash_init();
   db_read();
+  auth_create_fake_token();
 }