/* This file is part of Ping903
Copyright (C) 2020 Sergey Poznyakoff
Ping903 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 3, or (at your option)
any later version.
Ping903 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 Ping903. If not, see .
*/
#include
#include
#include
#include
#include
#include "sha1.h"
#include "md5.h"
#include "basicauth.h"
static pthread_mutex_t crypt_mutex = PTHREAD_MUTEX_INITIALIZER;
static int
crypt_match(const char *pass, const char *hash)
{
int res = 1;
char *cp;
pthread_mutex_lock(&crypt_mutex);
cp = crypt(pass, hash);
if (cp)
res = strcmp(cp, hash);
pthread_mutex_unlock(&crypt_mutex);
return res;
}
static int
plain_match(const char *pass, const char *hash)
{
return strcmp(pass, hash);
}
static int
apr_match(const char *pass, const char *hash)
{
char buf[120];
char *cp = apr_md5_encode(pass, hash, buf, sizeof(buf));
return cp ? strcmp(cp, hash) : 1;
}
#define SHA1_DIGEST_SIZE 20
static int
sha1_match(const char *pass, const char *hash)
{
char hashbuf[SHA1_DIGEST_SIZE], resbuf[SHA1_DIGEST_SIZE];
int n;
hash += 5; /* Skip past {SHA} */
n = base64_decode((const unsigned char *)hash, strlen(hash),
(unsigned char *)hashbuf, sizeof(hashbuf));
if (n < 0) {
/* FIXME: error("cannot decode %s", hash); */
return 1;
}
if (n != SHA1_DIGEST_SIZE) {
/* FIXME: error("bad hash length: %s %d", hash, n); */
return 1;
}
sha1_buffer(pass, strlen(pass), resbuf);
return memcmp(resbuf, hashbuf, SHA1_DIGEST_SIZE);
}
/* Matcher table */
struct matcher {
char *cm_pfx;
size_t cm_len;
int (*cm_match)(const char *, const char *);
};
static struct matcher match_tab[] = {
#define S(s) #s, sizeof(#s)-1
{ S($apr1$), apr_match },
{ S({SHA}), sha1_match },
{ "", 0, crypt_match },
{ "", 0, plain_match },
{ NULL }
};
static int
match(const char *pass, const char *hash)
{
struct matcher *p;
size_t plen = strlen(hash);
for (p = match_tab; p->cm_match; p++) {
if (p->cm_len < plen &&
memcmp(p->cm_pfx, hash, p->cm_len) == 0 &&
p->cm_match(pass, hash) == 0)
return 0;
}
return 1;
}
#define BASICPREF "Basic "
#define BASICLEN (sizeof(BASICPREF)-1)
BASICAUTH_RESULT
basicauth(char const *file, char const *input)
{
char buf[1024];
char lbuf[1024];
char *pass;
int n;
FILE *fp;
BASICAUTH_RESULT rc;
if (!input || strncmp(input, BASICPREF, BASICLEN))
return BASICAUTH_BAD_INPUT;
input += BASICLEN;
n = base64_decode((const unsigned char *)input, strlen(input),
(unsigned char *)buf, sizeof(buf));
if (n < 0) {
return BASICAUTH_BAD_INPUT;
} else if (n == sizeof(buf)) {
return BASICAUTH_BAD_INPUT;
}
buf[n] = 0;
pass = strchr(buf, ':');
if (!pass)
return BASICAUTH_BAD_INPUT;
*pass++ = 0;
fp = fopen(file, "r");
if (!fp)
return BASICAUTH_CANT_OPEN;
rc = BASICAUTH_DENY;
while (fgets(lbuf, sizeof(lbuf), fp)) {
char *p, *q;
for (p = lbuf; *p && (*p == ' ' || *p == '\t'); p++);
if (*p == '#')
continue;
q = p + strlen(p);
if (q == p)
continue;
if (q[-1] == '\n')
*--q = 0;
if (!*p)
continue;
q = strchr(p, ':');
if (!q)
continue;
*q++ = 0;
if (strcmp(p, buf))
continue;
rc = match(pass, q) ? BASICAUTH_DENY : BASICAUTH_ALLOW;
break;
}
fclose(fp);
return rc;
}