#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/uio.h>
#include "auth.h"
#include "auth_pts.h"
#include "cyrusdb.h"
#include "exitcodes.h"
#include "libcyr_cfg.h"
#include "lock.h"
#include "retry.h"
#include "strhash.h"
#include "xmalloc.h"
const char *auth_method_desc = "pts";
char *canonuser_id = NULL;
struct auth_state *canonuser_cache = NULL;
int ptload(const char *identifier,struct auth_state **state);
int auth_memberof(struct auth_state *auth_state,
const char *identifier)
{
int i;
unsigned idhash = strhash(identifier);
static unsigned anyonehash = 0;
anyonehash = !anyonehash ? strhash("anyone") : anyonehash;
if (!auth_state) {
if (!strcmp(identifier, "anyone")) return 1;
else if (!strcmp(identifier, "anonymous")) return 3;
else return 0;
}
if (idhash == anyonehash &&
!strcmp(identifier, "anyone")) return 1;
if (idhash == auth_state->userid.hash &&
!strcmp(identifier, auth_state->userid.id)) return 3;
for (i=0; i < auth_state->ngroups; i++)
if ( idhash == auth_state->groups[i].hash &&
(( !strncmp(identifier, "group:", 6) &&
!strcmp(identifier+6, auth_state->groups[i].id) ) ||
!strcmp(identifier, auth_state->groups[i].id)) )
return 2;
return 0;
}
char *auth_canonifyid(const char *identifier,
size_t len __attribute__((unused)))
{
static char retbuf[PTS_DB_KEYSIZE];
if(canonuser_id &&
(!strcmp(identifier, canonuser_id) || !strcmp(identifier, retbuf))) {
return retbuf;
} else if(canonuser_id) {
free(canonuser_id);
auth_freestate(canonuser_cache);
canonuser_id = NULL;
canonuser_cache = NULL;
}
if(!strcmp(identifier, "anyone") ||
!strcmp(identifier, "anonymous")) {
strlcpy(retbuf, identifier, sizeof(retbuf));
return retbuf;
} else if(ptload(identifier, &canonuser_cache)) {
return NULL;
} else {
canonuser_id = xstrdup(identifier);
strlcpy(retbuf, canonuser_cache->userid.id, sizeof(retbuf));
return retbuf;
}
}
struct auth_state *auth_newstate(const char *identifier)
{
struct auth_state *output = NULL;
if(canonuser_id &&
(!strcmp(identifier, canonuser_id) ||
!strcmp(identifier, canonuser_cache->userid.id))) {
free(canonuser_id);
canonuser_id = NULL;
output = canonuser_cache;
canonuser_cache = NULL;
} else {
if(!strcmp(identifier, "anyone") ||
!strcmp(identifier, "anonymous") ||
ptload(identifier, &output)) {
output =
(struct auth_state *)xzmalloc(sizeof(struct auth_state));
strlcpy(output->userid.id, identifier,
sizeof(output->userid.id));
output->userid.hash = strhash(identifier);
}
}
return output;
}
struct cyrusdb_backend *the_ptscache_db = NULL;
int ptload(const char *identifier, struct auth_state **state)
{
struct auth_state *fetched = NULL;
size_t id_len;
const char *data;
int dsize;
char fnamebuf[1024];
struct db *ptdb;
int s;
struct sockaddr_un srvaddr;
int r;
static char response[1024];
struct iovec iov[10];
int niov, n;
unsigned int start;
const char *config_dir =
libcyrus_config_getstring(CYRUSOPT_CONFIG_DIR);
if(the_ptscache_db == NULL) {
the_ptscache_db =
cyrusdb_fromname(libcyrus_config_getstring(CYRUSOPT_PTSCACHE_DB));
}
if(!state || *state) {
fatal("bad state pointer passed to ptload()", EC_TEMPFAIL);
}
strcpy(fnamebuf, config_dir);
strcat(fnamebuf, PTS_DBFIL);
r = the_ptscache_db->open(fnamebuf, CYRUSDB_CREATE, &ptdb);
if (r != 0) {
syslog(LOG_ERR, "DBERROR: opening %s: %s", fnamebuf,
cyrusdb_strerror(ret));
return -1;
}
id_len = strlen(identifier);
if(id_len > PTS_DB_KEYSIZE) {
syslog(LOG_ERR, "identifier too long in auth_newstate");
return -1;
}
r = the_ptscache_db->fetch(ptdb, identifier, id_len,
&data, &dsize, NULL);
if (r && r != CYRUSDB_NOTFOUND) {
syslog(LOG_ERR, "auth_newstate: error fetching record: %s",
cyrusdb_strerror(r));
goto done;
}
fetched = (struct auth_state *) data;
if(fetched) {
time_t now = time(NULL);
int timeout = libcyrus_config_getint(CYRUSOPT_PTS_CACHE_TIMEOUT);
syslog(LOG_DEBUG,
"ptload(): fetched cache record " \
"(mark %ld, current %ld, limit %ld)",
fetched->mark, now, now - timeout);
if (fetched->mark > (now - timeout)) {
*state = (struct auth_state *)xmalloc(dsize);
memcpy(*state, fetched, dsize);
goto done;
}
}
syslog(LOG_DEBUG, "ptload(): pinging ptloader");
s = socket(AF_UNIX, SOCK_STREAM, 0);
if (s == -1) {
syslog(LOG_ERR,
"ptload(): unable to create socket for ptloader: %m");
goto done;
}
if (libcyrus_config_getstring(CYRUSOPT_PTLOADER_SOCK))
strcpy(fnamebuf, libcyrus_config_getstring(CYRUSOPT_PTLOADER_SOCK));
else {
strcpy(fnamebuf, config_dir);
strcat(fnamebuf, PTS_DBSOCKET);
}
memset((char *)&srvaddr, 0, sizeof(srvaddr));
srvaddr.sun_family = AF_UNIX;
strcpy(srvaddr.sun_path, fnamebuf);
r = connect(s, (struct sockaddr *)&srvaddr, sizeof(srvaddr));
if (r == -1) {
syslog(LOG_ERR, "ptload(): can't connect to ptloader server: %m");
close(s);
goto done;
}
niov = 0;
WRITEV_ADD_TO_IOVEC(iov, niov, (char *) &id_len, sizeof(id_len));
WRITEV_ADD_TO_IOVEC(iov, niov, (char *) identifier, id_len);
retry_writev(s, iov, niov);
start = 0;
while (start < sizeof(response) - 1) {
n = read(s, response+start, sizeof(response) - 1 - start);
if (n < 1) break;
start += n;
}
close(s);
if (start <= 1 || strncmp(response, "OK", 2)) {
if(start > 1) {
syslog(LOG_ERR,
"ptload(): bad response from ptloader server: %s", response);
} else {
syslog(LOG_ERR, "ptload(): empty response from ptloader server");
}
goto done;
}
r = the_ptscache_db->fetch(ptdb, identifier, id_len,
&data, &dsize, NULL);
if (r != 0 || !data) {
syslog(LOG_ERR, "ptload(): error fetching record: %s"
"(did ptloader add the record?)",
cyrusdb_strerror(r));
goto done;
}
fetched = (struct auth_state *) data;
*state = (struct auth_state *)xmalloc(dsize);
memcpy(*state, fetched, dsize);
done:
the_ptscache_db->close(ptdb);
return (*state) ? 0 : -1;
}
void auth_freestate(struct auth_state *auth_state)
{
free(auth_state);
}