/* auth_pts.c -- PTLOADER authorization * $Id: auth_pts.c,v 1.4 2005/03/05 00:37:11 dasenbro Exp $ * Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name "Carnegie Mellon University" must not be used to * endorse or promote products derived from this software without * prior written permission. For permission or any other legal * details, please contact * Office of Technology Transfer * Carnegie Mellon University * 5000 Forbes Avenue * Pittsburgh, PA 15213-3890 * (412) 268-4387, fax: (412) 268-7395 * tech-transfer@andrew.cmu.edu * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Computing Services * at Carnegie Mellon University (http://www.cmu.edu/computing/)." * * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE * FOR ANY SPECIAL, 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. * * */ #include #include #include #include #include #include #include #include #include #include #include #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; /* Returns 0 on successful connection to ptloader/valid cache entry, * complete with allocated & filled in struct auth_state. * * state must be a NULL pointer when passed in */ int ptload(const char *identifier,struct auth_state **state); /* * Determine if the user is a member of 'identifier' * Returns one of: * 0 User does not match identifier * 1 identifier matches everybody * 2 User is in the group that is identifier * 3 User is identifer */ 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) { /* special case anonymous */ if (!strcmp(identifier, "anyone")) return 1; else if (!strcmp(identifier, "anonymous")) return 3; /* "anonymous" is not a member of any group */ else return 0; } /* is 'identifier' "anyone"? */ if (idhash == anyonehash && !strcmp(identifier, "anyone")) return 1; /* is 'identifier' me? */ if (idhash == auth_state->userid.hash && !strcmp(identifier, auth_state->userid.id)) return 3; /* is it a group i'm a member of ? */ 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; } /* * Convert 'identifier' into canonical form. * Returns a pointer to a static buffer containing the canonical form * or NULL if 'identifier' is invalid. */ 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))) { /* It's the currently cached user, return the previous result */ return retbuf; } else if(canonuser_id) { /* We've got a new one, invalidate our cache */ free(canonuser_id); auth_freestate(canonuser_cache); canonuser_id = NULL; canonuser_cache = NULL; } if(!strcmp(identifier, "anyone") || !strcmp(identifier, "anonymous")) { /* we can fill this in ourselves - no cacheing */ strlcpy(retbuf, identifier, sizeof(retbuf)); return retbuf; } else if(ptload(identifier, &canonuser_cache)) { /* Couldn't contact ptloader/database. Fail. */ return NULL; } else { canonuser_id = xstrdup(identifier); strlcpy(retbuf, canonuser_cache->userid.id, sizeof(retbuf)); return retbuf; } } /* * Produce an auth_state structure for the given identifier */ 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))) { /* It's the currently cached user, return the previous result */ free(canonuser_id); canonuser_id = NULL; output = canonuser_cache; canonuser_cache = NULL; } else { if(!strcmp(identifier, "anyone") || !strcmp(identifier, "anonymous") || ptload(identifier, &output)) { /* Anyone/Anonymous/ptload failure; fake it */ 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; /* Returns 0 on success */ 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); /* xxx this sucks, but it seems to be the only way to satisfy the linker */ 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; } /* fetch the current record for the user */ 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; } /* if it's expired (or nonexistant), * ask the ptloader to reload it and reread it */ 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)) { /* not expired; let's return it */ *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; } /* fetch the current record for the user */ 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; } /* ok, we got what we wanted */ fetched = (struct auth_state *) data; /* copy it into our structure */ *state = (struct auth_state *)xmalloc(dsize); memcpy(*state, fetched, dsize); done: /* close and unlock the database */ the_ptscache_db->close(ptdb); return (*state) ? 0 : -1; } void auth_freestate(struct auth_state *auth_state) { free(auth_state); }