aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore9
-rw-r--r--LICENSE15
-rw-r--r--Makefile70
-rw-r--r--config.mk8
-rw-r--r--libsecauth.h29
-rw-r--r--libsecauth_client_hash.c31
-rw-r--r--libsecauth_format_spec.c29
-rw-r--r--libsecauth_parse_spec.c84
-rw-r--r--libsecauth_server_hash.c55
-rw-r--r--mk/linux.mk4
-rw-r--r--mk/macos.mk4
-rw-r--r--mk/windows.mk4
12 files changed, 342 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e41dce2
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,9 @@
+*\#*
+*~
+*.o
+*.a
+*.lo
+*.so
+*.su
+*.dll
+*.dylib
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..c44b2d9
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,15 @@
+ISC License
+
+© 2021 Mattias Andrée <maandree@kth.se>
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..2ee23b3
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,70 @@
+.POSIX:
+
+LIB_MAJOR = 1
+LIB_MINOR = 0
+LIB_VERSION = $(LIB_MAJOR).$(LIB_MINOR)
+
+
+CONFIGFILE = config.mk
+include $(CONFIGFILE)
+
+OS = linux
+# Linux: linux
+# Mac OS: macos
+# Windows: windows
+include mk/$(OS).mk
+
+
+
+OBJ =\
+ libsecauth_client_hash.o\
+ libsecauth_format_spec.o\
+ libsecauth_parse_spec.o\
+ libsecauth_server_hash.o
+
+HDR =\
+ libsecauth.h
+
+LOBJ = $(OBJ:.o=.lo)
+
+
+all: libsecauth.a libsecauth.$(LIBEXT)
+$(OBJ): $($@:.o=.c) $(HDR)
+
+libsecauth.a: $(OBJ)
+ -rm -f -- $@
+ $(AR) rc $@ $(OBJ)
+ $(AR) -s $@
+
+libsecauth.$(LIBEXT): $(LOBJ)
+ $(CC) $(LIBFLAGS) $(LDFLAGS_METHODS) -o $@ $(LOBJ) $(LDFLAGS)
+
+.c.o:
+ $(CC) -c -o $@ $< $(CFLAGS) $(CPPFLAGS)
+
+.c.lo:
+ $(CC) -fPIC -c -o $@ $< $(CFLAGS) $(CPPFLAGS)
+
+install: libsecauth.a libsecauth.$(LIBEXT)
+ mkdir -p -- "$(DESTDIR)$(PREFIX)/lib"
+ mkdir -p -- "$(DESTDIR)$(PREFIX)/include"
+ cp -- libsecauth.a "$(DESTDIR)$(PREFIX)/lib/"
+ cp -- libsecauth.h "$(DESTDIR)$(PREFIX)/include/"
+ cp -- libsecauth.$(LIBEXT) "$(DESTDIR)$(PREFIX)/lib/libsecauth.$(LIBMINOREXT)"
+ ln -sf -- libsecauth.$(LIBMINOREXT) "$(DESTDIR)$(PREFIX)/lib/libsecauth.$(LIBMAJOREXT)"
+ ln -sf -- libsecauth.$(LIBMAJOREXT) "$(DESTDIR)$(PREFIX)/lib/libsecauth.$(LIBEXT)"
+
+uninstall:
+ -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libsecauth.$(LIBMAJOREXT)"
+ -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libsecauth.$(LIBMINOREXT)"
+ -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libsecauth.$(LIBEXT)"
+ -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libsecauth.a"
+ -rm -f -- "$(DESTDIR)$(PREFIX)/include/libsecauth.h"
+
+clean:
+ -rm -rf -- *.o *.a *.so *.lo *.su *.dll *.dylib
+
+.SUFFIXES:
+.SUFFIXES: .c .o .lo
+
+.PHONY: all install uninstall clean
diff --git a/config.mk b/config.mk
new file mode 100644
index 0000000..f785a40
--- /dev/null
+++ b/config.mk
@@ -0,0 +1,8 @@
+PREFIX = /usr
+MANPREFIX = $(PREFIX)/share/man
+
+CC = cc
+
+CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700
+CFLAGS = -std=c99 -Wall -Os
+LDFLAGS = -s
diff --git a/libsecauth.h b/libsecauth.h
new file mode 100644
index 0000000..67c2430
--- /dev/null
+++ b/libsecauth.h
@@ -0,0 +1,29 @@
+/* See LICENSE file for copyright and license details. */
+#ifndef LIBSECAUTH_H
+#define LIBSECAUTH_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+
+/* $secauth${$<prehash>$}${$<xferhash>$}$<client_rounds>$<server_rounds>${$<posthash>$}$<expected> */
+
+
+struct libsecauth_spec {
+ const char *prehash; /* secret if empty, all information is stored at client-side */
+ const char *xferhash;
+ const char *posthash;
+ const char *expected;
+ uint32_t client_rounds;
+ uint32_t server_rounds;
+};
+
+
+int libsecauth_parse_spec(struct libsecauth_spec *spec, char *settings);
+size_t libsecauth_format_spec(struct libsecauth_spec *spec, char *buffer, size_t buffer_size);
+
+char *libsecauth_client_hash(const struct libsecauth_spec *spec, const char *password);
+int libsecauth_server_hash(const struct libsecauth_spec *spec, const char *inhash, const char *pepper, char **resultp);
+
+
+#endif
diff --git a/libsecauth_client_hash.c b/libsecauth_client_hash.c
new file mode 100644
index 0000000..66bc8ac
--- /dev/null
+++ b/libsecauth_client_hash.c
@@ -0,0 +1,31 @@
+/* See LICENSE file for copyright and license details. */
+#include "libsecauth.h"
+
+#include <crypt.h>
+#include <string.h>
+
+
+char *
+libsecauth_client_hash(const struct libsecauth_spec *spec, const char *password)
+{
+ struct crypt_data hashbuf[2];
+ const char *hash = password;
+ uint32_t rounds;
+ size_t i;
+
+ memset(hashbuf, 0, sizeof(hashbuf));
+
+ if (spec->prehash && *spec->prehash) {
+ hash = crypt_r(password, spec->prehash, &hashbuf[1]);
+ if (!hash)
+ return NULL;
+ }
+
+ for (i = 0, rounds = spec->client_rounds; rounds--; i ^= 1) {
+ hash = crypt_r(hash, spec->xferhash, &hashbuf[i]);
+ if (!hash)
+ return NULL;
+ }
+
+ return strdup(hash);
+}
diff --git a/libsecauth_format_spec.c b/libsecauth_format_spec.c
new file mode 100644
index 0000000..83a08cf
--- /dev/null
+++ b/libsecauth_format_spec.c
@@ -0,0 +1,29 @@
+/* See LICENSE file for copyright and license details. */
+#include "libsecauth.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+
+
+size_t
+libsecauth_format_spec(struct libsecauth_spec *spec, char *buffer, size_t buffer_size)
+{
+ char client_rounds[sizeof("4294967295")];
+ char server_rounds[sizeof("4294967295")];
+ int i, expected_with_dollars;
+ client_rounds[0] = '\0';
+ server_rounds[0] = '\0';
+ if (spec->client_rounds)
+ sprintf(client_rounds, "%"PRIu32, spec->client_rounds);
+ if (spec->server_rounds)
+ sprintf(server_rounds, "%"PRIu32, spec->server_rounds);
+ expected_with_dollars = spec->expected && strchr(spec->expected, '$');
+ i = snprintf(buffer, buffer_size, "$secauth$%s%s%s$%s%s%s$%s$%s$%s%s%s$%s%s%s",
+ spec->prehash ? "{$" : "", spec->prehash ? spec->prehash : "", spec->prehash ? "$}" : "",
+ spec->xferhash ? "{$" : "", spec->xferhash ? spec->xferhash : "", spec->xferhash ? "$}" : "",
+ client_rounds, server_rounds,
+ spec->posthash ? "{$" : "", spec->posthash ? spec->posthash : "", spec->posthash ? "$}" : "",
+ expected_with_dollars ? "{$" : "", spec->expected ? spec->expected : "" , expected_with_dollars ? "$}" : "");
+ return i > 0 ? (size_t)i : 0;
+}
diff --git a/libsecauth_parse_spec.c b/libsecauth_parse_spec.c
new file mode 100644
index 0000000..2961268
--- /dev/null
+++ b/libsecauth_parse_spec.c
@@ -0,0 +1,84 @@
+/* See LICENSE file for copyright and license details. */
+#include "libsecauth.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <string.h>
+
+
+static char *
+get_subhash(char *s, char **endp)
+{
+ size_t depth = 0;
+ char *ret;
+
+ if (s[0] == '{' && s[1] == '$') {
+ s = ret = &s[2];
+ } else {
+ ret = s;
+ }
+ for (; s[0]; s++) {
+ if (s[0] == '{' && s[1] == '$' && s[-1] == '$') {
+ depth += 1;
+ } else if (s[0] == '}' && s[1] == '$' && s[-1] == '$') {
+ if (!depth)
+ return NULL;
+ if (!--depth) {
+ *s++ = '\0';
+ *s++ = '\0';
+ return ret;
+ }
+ } else if (s[0] == '$' && !depth) {
+ *s++ = '\0';
+ return ret;
+ }
+ }
+
+ return NULL;
+}
+
+static int
+strtou32(const char *s, uint32_t *valp)
+{
+ for (*valp = 0; isdigit(*s); s++) {
+ if (*valp > (UINT32_MAX - (uint32_t)(*s & 15)) / 10)
+ return -1;
+ *valp = *valp * 10 + (uint32_t)(*s & 15);
+ }
+ return *s ? -1 : 0;
+}
+
+int
+libsecauth_parse_spec(struct libsecauth_spec *spec, char *s)
+{
+ const char *client_rounds, *server_rounds;
+ size_t slen = strlen(s);
+
+ memset(spec, 0, sizeof(*spec));
+
+ if (strncmp(s, "$secauth$", sizeof("$secauth$") - 1))
+ goto invalid;
+ s = &s[sizeof("$secauth$") - 1];
+
+ spec->prehash = get_subhash(s, &s);
+ spec->xferhash = get_subhash(s, &s);
+ client_rounds = get_subhash(s, &s);
+ server_rounds = get_subhash(s, &s);
+ spec->posthash = get_subhash(s, &s);
+ spec->expected = get_subhash(s, &s);
+
+ if (client_rounds && strtou32(client_rounds, &spec->client_rounds))
+ goto invalid;
+ if (server_rounds && strtou32(server_rounds, &spec->server_rounds))
+ goto invalid;
+
+ if (*s)
+ goto invalid;
+
+ return 0;
+
+invalid:
+ memset(s, 0, slen);
+ errno = EINVAL;
+ return -1;
+}
diff --git a/libsecauth_server_hash.c b/libsecauth_server_hash.c
new file mode 100644
index 0000000..6f0d4f0
--- /dev/null
+++ b/libsecauth_server_hash.c
@@ -0,0 +1,55 @@
+/* See LICENSE file for copyright and license details. */
+#include "libsecauth.h"
+
+#include <crypt.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+int
+libsecauth_server_hash(const struct libsecauth_spec *spec, const char *inhash, const char *pepper, char **resultp)
+{
+ struct crypt_data hashbuf[2];
+ const char *hash = inhash, *result;
+ char *posthash = NULL, *p;
+ uint32_t rounds;
+ size_t i = 0;
+
+ *resultp = NULL;
+ memset(hashbuf, 0, sizeof(hashbuf));
+
+ for (i = 0, rounds = spec->server_rounds; rounds--; i ^= 1) {
+ hash = crypt_r(hash, spec->xferhash, &hashbuf[i]);
+ if (!hash)
+ return -1;
+ }
+
+ if (pepper) {
+ posthash = malloc(strlen(spec->posthash) + strlen(pepper) + 2);
+ if (!posthash)
+ return -1;
+ p = stpcpy(posthash, spec->posthash);
+ if (*posthash && p[-1] == '$')
+ p -= 1;
+ stpcpy(p, pepper);
+ }
+
+ hash = crypt_r(hash, posthash ? posthash : spec->posthash, &hashbuf[i]);
+ free(posthash);
+ if (!hash)
+ return -1;
+
+ result = strrchr(hash, '$');
+ result = result ? &result[1] : hash;
+
+ if (resultp) {
+ *resultp = strdup(result);
+ if (!*resultp)
+ return -1;
+ }
+
+ if (!spec->expected || !*spec->expected)
+ return 0;
+
+ return !strcmp(result, spec->expected);
+}
diff --git a/mk/linux.mk b/mk/linux.mk
new file mode 100644
index 0000000..24b174e
--- /dev/null
+++ b/mk/linux.mk
@@ -0,0 +1,4 @@
+LIBEXT = so
+LIBFLAGS = -shared -Wl,-soname,libsecauth.$(LIBEXT).$(LIB_MAJOR)
+LIBMAJOREXT = $(LIBEXT).$(LIB_MAJOR)
+LIBMINOREXT = $(LIBEXT).$(LIB_VERSION)
diff --git a/mk/macos.mk b/mk/macos.mk
new file mode 100644
index 0000000..bd92de6
--- /dev/null
+++ b/mk/macos.mk
@@ -0,0 +1,4 @@
+LIBEXT = dylib
+LIBFLAGS = -dynamiclib
+LIBMAJOREXT = $(LIB_MAJOR).$(LIBEXT)
+LIBMINOREXT = $(LIB_VERSION).$(LIBEXT)
diff --git a/mk/windows.mk b/mk/windows.mk
new file mode 100644
index 0000000..e9602e1
--- /dev/null
+++ b/mk/windows.mk
@@ -0,0 +1,4 @@
+LIBEXT = dll
+LIBFLAGS = -mdll
+LIBMAJOREXT = $(LIB_MAJOR).$(LIBEXT)
+LIBMINOREXT = $(LIB_VERSION).$(LIBEXT)