/*
* ProFTPD: mod_sql -- SQL frontend
* Copyright (c) 1998-1999 Johnie Ingram.
* Copyright (c) 2001 Andrew Houghton.
* Copyright (c) 2004-2005 TJ Saunders
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*
* As a special exemption, Andrew Houghton and other respective copyright
* holders give permission to link this program with OpenSSL, and distribute
* the resulting executable, without including the source code for OpenSSL in
* the source distribution.
*
* $Id: mod_sql.c,v 1.101 2005/12/13 17:54:53 castaglia Exp $
*/
#include "conf.h"
#include "privs.h"
#include "mod_sql.h"
#define MOD_SQL_VERSION "mod_sql/4.2.1"
#if defined(HAVE_CRYPT_H) && !defined(AIX4) && !defined(AIX5)
# include <crypt.h>
#endif
/* Uncomment the following define to allow OpenSSL hashed password checking;
* you'll also need to link with OpenSSL's crypto library ( -lcrypto )
*/
/* #define HAVE_OPENSSL */
#ifdef HAVE_OPENSSL
# include <openssl/evp.h>
#endif
/* default information for tables and fields */
#define MOD_SQL_DEF_USERTABLE "users"
#define MOD_SQL_DEF_USERNAMEFIELD "userid"
#define MOD_SQL_DEF_USERUIDFIELD "uid"
#define MOD_SQL_DEF_USERGIDFIELD "gid"
#define MOD_SQL_DEF_USERPASSWORDFIELD "passwd"
#define MOD_SQL_DEF_USERSHELLFIELD "shell"
#define MOD_SQL_DEF_USERHOMEDIRFIELD "homedir"
#define MOD_SQL_DEF_GROUPTABLE "groups"
#define MOD_SQL_DEF_GROUPNAMEFIELD "groupname"
#define MOD_SQL_DEF_GROUPGIDFIELD "gid"
#define MOD_SQL_DEF_GROUPMEMBERSFIELD "members"
/* default minimum id / default uid / default gid info.
* uids and gids less than MOD_SQL_MIN_USER_UID and
* MOD_SQL_MIN_USER_GID, respectively, get automatically
* mapped to the defaults, below. These can be
* overridden using directives
*/
#define MOD_SQL_MIN_USER_UID 999
#define MOD_SQL_MIN_USER_GID 999
#define MOD_SQL_DEF_UID 65533
#define MOD_SQL_DEF_GID 65533
#define MOD_SQL_BUFSIZE 32
/* Named Query defines */
#define SQL_SELECT_C "SELECT"
#define SQL_INSERT_C "INSERT"
#define SQL_UPDATE_C "UPDATE"
#define SQL_FREEFORM_C "FREEFORM"
/* SQLEngine flags */
#define SQL_ENGINE_FL_AUTH 0x001
#define SQL_ENGINE_FL_LOG 0x002
/* authmask defines */
#define SQL_AUTH_USERS (1<<0)
#define SQL_AUTH_GROUPS (1<<1)
#define SQL_AUTH_USERSET (1<<4)
#define SQL_AUTH_GROUPSET (1<<5)
#define SQL_FAST_USERSET (1<<6)
#define SQL_FAST_GROUPSET (1<<7)
#define SQL_GROUPS (cmap.authmask & SQL_AUTH_GROUPS)
#define SQL_USERS (cmap.authmask & SQL_AUTH_USERS)
#define SQL_GROUPSET (cmap.authmask & SQL_AUTH_GROUPSET)
#define SQL_USERSET (cmap.authmask & SQL_AUTH_USERSET)
#define SQL_FASTGROUPS (cmap.authmask & SQL_FAST_GROUPSET)
#define SQL_FASTUSERS (cmap.authmask & SQL_FAST_USERSET)
/*
* externs, function signatures.. whatever necessary to make
* the compiler happy..
*/
extern pr_response_t *resp_list,*resp_err_list;
module sql_module;
static char *_sql_where(pool *p, int cnt, ...);
MODRET cmd_getgrent(cmd_rec *);
MODRET cmd_setgrent(cmd_rec *);
MODRET sql_lookup(cmd_rec *);
static pool *sql_pool = NULL;
/*
* cache typedefs
*/
#define CACHE_SIZE 13
typedef struct cache_entry {
struct cache_entry *list_next;
struct cache_entry *bucket_next;
void *data;
} cache_entry_t;
/* this struct holds invariant information for the current session */
static struct {
/*
* info valid after getpwnam
*/
char *authuser; /* current authorized user */
struct passwd *authpasswd; /* and their passwd struct */
/*
* generic status information
*/
int engine; /* is mod_sql on? */
int authmask; /* authentication mask.
* see set_sqlauthenticate for info */
/*
* user table and field information
*/
char *usrtable; /* user info table name */
char *usrfield; /* user name field */
char *pwdfield; /* user password field */
char *uidfield; /* user uid field */
char *gidfield; /* user gid field */
char *homedirfield; /* user homedir field */
char *shellfield; /* user login shell field */
char *userwhere; /* users where clause */
char *usercustom; /* custom users query */
/*
* group table and field information
*/
char *grptable; /* group info table name */
char *grpfield; /* group name field */
char *grpgidfield; /* group gid field */
char *grpmembersfield; /* group members field */
char *groupwhere; /* groups where clause */
char *groupcustom; /* custom groups query */
/*
* other information
*/
array_header *authlist; /* auth handler list */
char *defaulthomedir; /* default homedir if no field specified */
int buildhomedir; /* create homedir if it doesn't exist? */
uid_t minid; /* users UID must be this or greater */
uid_t minuseruid; /* users UID must be this or greater */
gid_t minusergid; /* users UID must be this or greater */
uid_t defaultuid; /* default UID if none in database */
gid_t defaultgid; /* default GID if none in database */
cache_entry_t *curr_group; /* next group in group array for getgrent */
cache_entry_t *curr_passwd; /* next passwd in passwd array for getpwent */
int group_cache_filled;
int passwd_cache_filled;
/* Cache negative, as well as positive, lookups */
unsigned char negative_cache;
/*
* mod_ratio data -- someday this needs to be removed from mod_sql
*/
char *sql_fstor; /* fstor int(11) NOT NULL DEFAULT '0', */
char *sql_fretr; /* fretr int(11) NOT NULL DEFAULT '0', */
char *sql_bstor; /* bstor int(11) NOT NULL DEFAULT '0', */
char *sql_bretr; /* bretr int(11) NOT NULL DEFAULT '0', */
char *sql_frate; /* frate int(11) NOT NULL DEFAULT '5', */
char *sql_fcred; /* fcred int(2) NOT NULL DEFAULT '15', */
char *sql_brate; /* brate int(11) NOT NULL DEFAULT '5', */
char *sql_bcred; /* bcred int(2) NOT NULL DEFAULT '150000', */
/*
* precomputed strings
*/
char *usrfields;
char *grpfields;
}
cmap;
struct sql_backend {
struct sql_backend *next, *prev;
const char *backend;
cmdtable *cmdtab;
};
static struct sql_backend *sql_backends = NULL;
static unsigned int sql_nbackends = 0;
static cmdtable *sql_cmdtable = NULL;
/*
* cache functions
*/
typedef unsigned int ( * val_func ) ( const void * );
typedef int ( * cmp_func ) ( const void *, const void * );
typedef struct {
/* memory pool for this object */
pool *pool;
/* cache buckets */
cache_entry_t *buckets[ CACHE_SIZE ];
/* cache functions */
val_func hash_val;
cmp_func cmp;
/* list pointers */
cache_entry_t *head;
/* list size */
unsigned int nelts;
} cache_t;
cache_t *group_name_cache;
cache_t *group_gid_cache;
cache_t *passwd_name_cache;
cache_t *passwd_uid_cache;
static cache_t *make_cache( pool *p, val_func hash_val, cmp_func cmp )
{
cache_t *res;
if ( ( p == NULL ) || ( hash_val == NULL ) ||
( cmp == NULL ) )
return NULL;
res = ( cache_t * ) pcalloc( p, sizeof( cache_t ) );
res->pool = p;
res->hash_val = hash_val;
res->cmp = cmp;
res->head = NULL;
res->nelts = 0;
return res;
}
static cache_entry_t *cache_addentry( cache_t *cache, void *data )
{
cache_entry_t *entry;
int hashval;
if ( ( cache == NULL ) || ( data == NULL ) )
return NULL;
/* create the entry */
entry = ( cache_entry_t * ) pcalloc( cache->pool,
sizeof( cache_entry_t ) );
entry->data = data;
/* deal with the list */
if ( cache->head == NULL ) {
cache->head = entry;
} else {
entry->list_next = cache->head;
cache->head = entry;
}
/* deal with the buckets */
hashval = cache->hash_val( data ) % CACHE_SIZE;
if ( cache->buckets[ hashval ] == NULL ) {
cache->buckets[ hashval ] = entry;
} else {
entry->bucket_next = cache->buckets[ hashval ];
cache->buckets[ hashval ] = entry;
}
cache->nelts++;
return entry;
}
static void *cache_findvalue( cache_t *cache, void *data )
{
cache_entry_t *entry;
int hashval;
if ( ( cache == NULL ) || ( data == NULL ) ) return NULL;
hashval = cache->hash_val( data ) % CACHE_SIZE;
entry = cache->buckets[ hashval ];
while ( entry != NULL ) {
if ( cache->cmp( data, entry->data ) )
break;
else
entry = entry->bucket_next;
}
return ( ( entry == NULL ) ? NULL : entry->data );
}
cmd_rec *_sql_make_cmd(pool *p, int argc, ...) {
register unsigned int i = 0;
pool *newpool = NULL;
cmd_rec *cmd = NULL;
va_list args;
newpool = make_sub_pool(p);
cmd = pcalloc(newpool, sizeof(cmd_rec));
cmd->argc = argc;
cmd->stash_index = -1;
cmd->pool = newpool;
cmd->argv = pcalloc(newpool, sizeof(void *) * (argc + 1));
cmd->tmp_pool = newpool;
cmd->server = main_server;
va_start(args, argc);
for (i = 0; i < argc; i++)
cmd->argv[i] = (void *) va_arg(args, char *);
va_end(args);
cmd->argv[argc] = NULL;
return cmd;
}
static modret_t *_sql_check_response(modret_t *mr) {
if (!MODRET_ISERROR(mr))
return mr;
sql_log(DEBUG_WARN, "%s", "unrecoverable backend error");
sql_log(DEBUG_WARN, "error: '%s'", mr->mr_numeric);
sql_log(DEBUG_WARN, "message: '%s'", mr->mr_message);
end_login(1);
/* make the compiler happy */
return NULL;
}
static modret_t *_sql_dispatch(cmd_rec *cmd, char *cmdname) {
modret_t *mr = NULL;
register unsigned int i = 0;
for (i = 0; sql_cmdtable[i].command; i++) {
if (strcmp(cmdname, sql_cmdtable[i].command) == 0) {
pr_signals_block();
mr = sql_cmdtable[i].handler(cmd);
pr_signals_unblock();
return mr;
}
}
sql_log(DEBUG_WARN, "unknown backend handler '%s'", cmdname);
return ERROR(cmd);
}
static struct sql_backend *sql_get_backend(const char *backend) {
struct sql_backend *sb;
if (!sql_backends)
return NULL;
for (sb = sql_backends; sb; sb = sb->next) {
if (strcasecmp(sb->backend, backend) == 0)
return sb;
}
return NULL;
}
/* This function is used by mod_sql backends, to register their
* individual backend command tables with the main mod_sql module.
*/
int sql_register_backend(const char *backend, cmdtable *cmdtab) {
struct sql_backend *sb;
if (!backend || !cmdtab) {
errno = EINVAL;
return -1;
}
if (!sql_pool) {
sql_pool = make_sub_pool(permanent_pool);
pr_pool_tag(sql_pool, MOD_SQL_VERSION);
}
/* Check to see if this backend has already been registered. */
sb = sql_get_backend(backend);
if (sb) {
errno = EEXIST;
return -1;
}
sb = pcalloc(sql_pool, sizeof(struct sql_backend));
sb->backend = backend;
sb->cmdtab = cmdtab;
if (sql_backends)
sb->next = sql_backends;
else
sb->next = NULL;
sql_backends = sb;
sql_nbackends++;
return 0;
}
/* Used by mod_sql backends to unregister their backend command tables
* from the main mod_sql module.
*/
int sql_unregister_backend(const char *backend) {
struct sql_backend *sb;
if (!backend) {
errno = EINVAL;
return -1;
}
/* Check to see if this backend has been registered. */
sb = sql_get_backend(backend);
if (!sb) {
errno = ENOENT;
return -1;
}
#if !defined(PR_SHARED_MODULE)
/* If there is only one registered backend, it cannot be removed.
*/
if (sql_nbackends == 1) {
errno = EPERM;
return -1;
}
/* Be sure to handle the case where this is the currently active backend. */
if (sql_cmdtable &&
sb->cmdtab == sql_cmdtable) {
errno = EACCES;
return -1;
}
#endif
/* Remove this backend from the linked list. */
if (sb->prev)
sb->prev->next = sb->next;
else
/* This backend is the start of the sql_backends list (prev is NULL),
* so we need to update the list head pointer as well.
*/
sql_backends = sb->next;
if (sb->next)
sb->next->prev = sb->prev;
sb->prev = sb->next = NULL;
sql_nbackends--;
/* NOTE: a counter should be kept of the number of unregistrations,
* as the memory for a registration is not freed on unregistration.
*/
return 0;
}
/*****************************************************************
*
* AUTHENTICATION FUNCTIONS
*
*****************************************************************/
static modret_t *check_auth_crypt(cmd_rec *cmd, const char *c_clear,
const char *c_hash) {
int success = 0;
if (*c_hash == '\0')
return ERROR_INT(cmd, PR_AUTH_BADPWD);
success = !strcmp((char *) crypt(c_clear, c_hash), c_hash);
return success ? HANDLED(cmd) : ERROR_INT(cmd, PR_AUTH_BADPWD);
}
static modret_t *check_auth_plaintext(cmd_rec *cmd, const char *c_clear,
const char *c_hash) {
int success = 0;
if (*c_hash == '\0')
return ERROR_INT(cmd, PR_AUTH_BADPWD);
success = !strcmp(c_clear, c_hash);
return success ? HANDLED(cmd) : ERROR_INT(cmd, PR_AUTH_BADPWD);
}
static modret_t *check_auth_empty(cmd_rec *cmd, const char *c_clear,
const char *c_hash) {
int success = 0;
success = !strcmp(c_hash, "");
return success ? HANDLED(cmd) : ERROR_INT(cmd, PR_AUTH_BADPWD);
}
static modret_t *check_auth_backend(cmd_rec *cmd, const char *c_clear,
const char *c_hash) {
modret_t *mr = NULL;
if (*c_hash == '\0')
return ERROR_INT(cmd, PR_AUTH_BADPWD);
mr = _sql_dispatch(_sql_make_cmd(cmd->tmp_pool, 3, "default", c_clear,
c_hash), "sql_checkauth");
return mr;
}
#ifdef HAVE_OPENSSL
static modret_t *check_auth_openssl(cmd_rec *cmd, const char *c_clear,
const char *c_hash) {
/*
* c_clear : plaintext password provided by user
* c_hash : combination digest name and hashed
* value, of the form {digest}hash
*/
EVP_MD_CTX md_ctxt;
EVP_ENCODE_CTX base64_ctxt;
const EVP_MD *md;
unsigned char mdval[EVP_MAX_MD_SIZE];
int mdlen, res;
char buf[EVP_MAX_KEY_LENGTH];
char *digestname; /* ptr to name of the digest function */
char *hashvalue; /* ptr to hashed value we're comparing to */
char *copyhash; /* temporary copy of the c_hash string */
if (c_hash[0] != '{')
return ERROR_INT(cmd, PR_AUTH_BADPWD);
/* We need a copy of c_hash. */
copyhash = pstrdup(cmd->tmp_pool, c_hash);
digestname = copyhash + 1;
hashvalue = (char *) strchr(copyhash, '}');
if (hashvalue == NULL)
return ERROR_INT(cmd, PR_AUTH_BADPWD);
*hashvalue = '\0';
hashvalue++;
OpenSSL_add_all_digests();
md = EVP_get_digestbyname(digestname);
if (!md)
return ERROR_INT(cmd, PR_AUTH_BADPWD);
EVP_DigestInit(&md_ctxt, md);
EVP_DigestUpdate(&md_ctxt, c_clear, strlen(c_clear));
EVP_DigestFinal(&md_ctxt, mdval, &mdlen);
EVP_EncodeInit(&base64_ctxt);
EVP_EncodeBlock(buf, mdval, mdlen);
res = strcmp(buf, hashvalue);
return res ? ERROR_INT(cmd, PR_AUTH_BADPWD) : HANDLED(cmd);
}
#endif
/*
* support for general-purpose authentication schemes
*/
#define PLAINTEXT_AUTH_FLAG 1<<0
#define CRYPT_AUTH_FLAG 1<<1
#define BACKEND_AUTH_FLAG 1<<2
#define EMPTY_AUTH_FLAG 1<<3
#ifdef HAVE_OPENSSL
#define OPENSSL_AUTH_FLAG 1<<4
#endif
typedef modret_t *(*auth_func_ptr) (cmd_rec *, const char *, const char *);
typedef struct {
char *name;
auth_func_ptr check_function;
int flag;
}
auth_type_entry;
static auth_type_entry supported_auth_types[] = {
{"Plaintext", check_auth_plaintext, PLAINTEXT_AUTH_FLAG},
{"Crypt", check_auth_crypt, CRYPT_AUTH_FLAG},
{"Backend", check_auth_backend, BACKEND_AUTH_FLAG},
{"Empty", check_auth_empty, EMPTY_AUTH_FLAG},
#ifdef HAVE_OPENSSL
{"OpenSSL", check_auth_openssl, OPENSSL_AUTH_FLAG},
#endif
/*
* add additional encryption types below
*/
{NULL, NULL, 0}
};
static auth_type_entry *get_auth_entry(char *name) {
auth_type_entry *ate = supported_auth_types;
while (ate->name) {
if (strcasecmp(ate->name, name) == 0)
return ate;
ate++;
}
return NULL;
}
/*****************************************************************
*
* INTERNAL HELPER FUNCTIONS
*
*****************************************************************/
/* find who core thinks is the user, and return a (backend-escaped)
* version of that name */
static char *_sql_realuser(cmd_rec *cmd) {
modret_t *mr = NULL;
char *user = NULL;
/* this is the userid given by the user */
user = (char *) get_param_ptr(main_server->conf, C_USER, FALSE);
/* do we need to check for useralias?
* see mod_time.c, get_user_cmd_times() */
mr = _sql_dispatch(_sql_make_cmd(cmd->tmp_pool, 2, "default", user),
"sql_escapestring");
_sql_check_response(mr);
return mr ? (char *) mr->data : NULL;
}
static char *_sql_where(pool *p, int cnt, ...) {
int tcnt;
int flag;
int len;
char *res, *tchar;
va_list dummy;
flag = 0;
len = 0;
va_start(dummy, cnt);
for (tcnt = 0; tcnt < cnt; tcnt++) {
res = va_arg(dummy, char *);
if (res != NULL && *res != '\0') {
if (flag++)
len += 5;
len += strlen(res);
len += 2;
}
}
va_end(dummy);
if (!len)
return NULL;
res = pcalloc(p, sizeof(char) * (len+1));
flag = 0;
va_start(dummy, cnt);
for (tcnt = 0; tcnt < cnt; tcnt++) {
tchar = va_arg(dummy, char *);
if (tchar != NULL && *tchar != '\0') {
if (flag++)
sstrcat(res, " and ", len+1);
sstrcat(res, "(", len+1);
sstrcat(res, tchar, len+1);
sstrcat(res, ")", len+1);
}
}
va_end(dummy);
return res;
}
static int _sql_strcmp(const char *s1, const char *s2) {
if ((s1 == NULL) || (s2 == NULL))
return 1;
return strcmp(s1, s2);
}
static unsigned int _group_gid(const void *val) {
if (val == NULL)
return 0;
return ((struct group *) val)->gr_gid;
}
static unsigned int _group_name(const void *val) {
char *name;
int cnt;
unsigned int nameval = 0;
if (val == NULL)
return 0;
name = ((struct group *) val)->gr_name;
if (name == NULL)
return 0;
for (cnt = 0; cnt < strlen(name); cnt++) {
nameval += name[cnt];
}
return nameval;
}
static int _groupcmp(const void *val1, const void *val2) {
if ((val1 == NULL) || (val2 == NULL))
return 0;
/* either the groupnames match or the gids match */
if (_sql_strcmp(((struct group *) val1)->gr_name,
((struct group *) val2)->gr_name) == 0)
return 1;
if (((struct group *) val1)->gr_gid == ((struct group *) val2)->gr_gid)
return 1;
return 0;
}
static unsigned int _passwd_uid(const void *val) {
if (val == NULL)
return 0;
return ((struct passwd *) val)->pw_uid;
}
static unsigned int _passwd_name(const void *val) {
char *name;
int cnt;
unsigned int nameval = 0;
if (val == NULL)
return 0;
name = ((struct passwd *) val)->pw_name;
if (name == NULL)
return 0;
for (cnt = 0; cnt < strlen(name); cnt++) {
nameval += name[cnt];
}
return nameval;
}
static int _passwdcmp(const void *val1, const void *val2) {
if ((val1 == NULL) || (val2 == NULL))
return 0;
/* either the usernames match or the uids match */
if (_sql_strcmp(((struct passwd *) val1)->pw_name,
((struct passwd *) val2)->pw_name) == 0)
return 1;
if (((struct passwd *) val1)->pw_uid == ((struct passwd *) val2)->pw_uid)
return 1;
return 0;
}
static void show_group(pool *p, struct group *g) {
char **member = NULL, *members = "";
if (g == NULL) {
sql_log(DEBUG_INFO, "%s", "NULL group to show_group()");
return;
}
member = g->gr_mem;
while (*member != NULL) {
members = pstrcat(p, members, *members ? ", " : "", *member, NULL);
member++;
}
sql_log(DEBUG_INFO, "+ grp.gr_name : %s", g->gr_name);
sql_log(DEBUG_INFO, "+ grp.gr_gid : %lu", (unsigned long) g->gr_gid);
sql_log(DEBUG_INFO, "+ grp.gr_mem : %s", members);
return;
}
static void show_passwd(struct passwd *p) {
if (p == NULL) {
sql_log(DEBUG_INFO, "%s", "NULL passwd to show_passwd()");
return;
}
sql_log(DEBUG_INFO, "+ pwd.pw_name : %s", p->pw_name);
sql_log(DEBUG_INFO, "+ pwd.pw_uid : %lu", (unsigned long) p->pw_uid);
sql_log(DEBUG_INFO, "+ pwd.pw_gid : %lu", (unsigned long) p->pw_gid);
sql_log(DEBUG_INFO, "+ pwd.pw_dir : %s", p->pw_dir);
sql_log(DEBUG_INFO, "+ pwd.pw_shell : %s", p->pw_shell);
return;
}
static int build_homedir(cmd_rec *cmd, char *path, mode_t omode, uid_t uid,
gid_t gid) {
struct stat st;
mode_t old_umask;
int retval = 0;
char *local_ptr;
char *local_path;
int userdir_flag = 0;
gid_t p_gid;
uid_t p_uid;
sql_log(DEBUG_FUNC, ">>> build_homedir(%s,omode,%i,%i)", path, uid, gid);
/* we assume we're handed a null-terminated string defining the
* user's home directory. we walk it, directory by directory,
* creating it if it doesn't exist. path must start with '/'
*/
if (path[0] != '/') {
sql_log(DEBUG_WARN, "%s", "no '/' at start of user's homedir");
sql_log(DEBUG_FUNC, "%s", "<<< build_homedir");
return -1;
}
/* sanity check -- make sure the path doesn't exist */
if (!pr_fsio_stat(path, &st)) {
sql_log(DEBUG_WARN, "%s", "user's homedir already exists");
sql_log(DEBUG_FUNC, "%s", "<<< build_homedir");
return 0;
} else if (errno != ENOENT) {
sql_log(DEBUG_WARN, "problem with stat of user's homedir: %s",
strerror(errno));
sql_log(DEBUG_FUNC, "%s", "<<< build_homedir");
return -1;
}
/* make our local copy of path, adding a '/' if necessary..
* after this call, we're *guaranteed* a terminating '/'. We use
* this info later. */
if ( path[(strlen(path) - 1)] == '/' )
local_path = pstrdup(cmd->tmp_pool, path);
else
local_path = pstrcat(cmd->tmp_pool, path, "/", NULL);
/* gain root for dir creation process */
p_gid = getegid();
p_uid = geteuid();
PRIVS_ROOT
/* skip the leading '/' */
local_ptr = local_path + 1;
while ( ( local_ptr = strchr( local_ptr, '/' ) ) != NULL ) {
*local_ptr = '\0';
if ( *(local_ptr + 1) == '\0' )
userdir_flag = 1;
if ( pr_fsio_stat( local_path, &st ) ) {
/* if the stat failed.. */
if (errno == ENOENT) {
/* and it's 'cause the directory doesn't exist */
if ( !userdir_flag ) {
/* if it's an intermediate dir */
if ( pr_fsio_mkdir(local_path, S_IRWXU | S_IRWXG | S_IRWXO ) ) {
PRIVS_RELINQUISH
return -1;
} else {
pr_fsio_chown(local_path, p_uid, p_gid );
}
} else {
/* this is the user's homedir, and the final directory */
old_umask = umask(0);
umask( old_umask & ~(S_IWUSR | S_IXUSR | S_IRUSR) );
if ( pr_fsio_mkdir(local_path, omode) ) {
umask( old_umask );
PRIVS_RELINQUISH
return -1;
} else {
pr_fsio_chown(local_path, uid, gid);
}
umask( old_umask );
}
} else {
/* we failed for a reason other than no such
* directory, so we return an error */
PRIVS_RELINQUISH
return -1;
}
}
/* fix local_ptr, and bump it */
*local_ptr = '/';
local_ptr++;
}
/* relinquish root privileges */
PRIVS_RELINQUISH
sql_log(DEBUG_FUNC, "%s", "<<< build_homedir");
return (retval);
}
/* _sql_addpasswd: creates a passwd and adds it to the passwd struct
* cache if it doesn't already exist. Returns the created passwd
* struct, or the pre-existing struct if there was one.
*
* DOES NOT CHECK ARGUMENTS. CALLING FUNCTIONS NEED TO MAKE SURE
* THEY PASS VALID DATA
*/
static struct passwd *_sql_addpasswd(cmd_rec *cmd, char *username,
char *password, uid_t uid, gid_t gid, char *shell, char *dir) {
struct passwd *cached = NULL;
struct passwd *pwd = NULL;
pwd = pcalloc(cmd->tmp_pool, sizeof(struct passwd));
pwd->pw_uid = uid;
pwd->pw_name = username;
/* check to make sure the entry doesn't exist in the cache */
if ( ((cached = (struct passwd *)
cache_findvalue(passwd_name_cache, pwd)) != NULL)) {
pwd = cached;
sql_log(DEBUG_INFO, "cache hit for user '%s'", pwd->pw_name);
} else {
pwd = pcalloc(sql_pool, sizeof(struct passwd));
if (username)
pwd->pw_name = pstrdup(sql_pool, username);
if (password)
pwd->pw_passwd = pstrdup(sql_pool, password);
pwd->pw_uid = uid;
pwd->pw_gid = gid;
if (shell)
pwd->pw_shell = pstrdup(sql_pool, shell);
if (dir)
pwd->pw_dir = pstrdup(sql_pool, dir);
cache_addentry(passwd_name_cache, pwd);
cache_addentry(passwd_uid_cache, pwd);
sql_log(DEBUG_INFO, "cache miss for user '%s'", pwd->pw_name);
sql_log(DEBUG_INFO, "user '%s' cached", pwd->pw_name);
show_passwd(pwd);
}
return pwd;
}
static struct passwd *_sql_getpasswd(cmd_rec *cmd, struct passwd *p) {
sql_data_t *sd = NULL;
modret_t *mr = NULL;
struct passwd *pwd = NULL;
char uidstr[MOD_SQL_BUFSIZE] = {'\0'};
char *usrwhere, *where;
char *realname = NULL;
int i = 0;
char *username = NULL;
char *password = NULL;
char *shell = NULL;
char *dir = NULL;
uid_t uid = 0;
gid_t gid = 0;
if (p == NULL) {
sql_log(DEBUG_WARN, "%s", "_sql_getpasswd called with NULL passwd struct");
sql_log(DEBUG_WARN, "%s", "THIS SHOULD NEVER HAPPEN");
return NULL;
}
if (!cmap.homedirfield &&
!cmap.defaulthomedir)
return NULL;
/* check to see if the passwd already exists in one of the passwd caches */
if (((pwd = (struct passwd *)
cache_findvalue(passwd_name_cache, p)) != NULL) ||
((pwd = (struct passwd *)
cache_findvalue(passwd_uid_cache, p)) != NULL)) {
sql_log(DEBUG_AUTH, "cache hit for user '%s'", pwd->pw_name);
/* Check for negatively cached passwds, which will have NULL
* passwd/home/shell.
*/
if (!pwd->pw_dir) {
sql_log(DEBUG_AUTH, "negative cache entry for user '%s'", pwd->pw_name);
return NULL;
}
return pwd;
}
if (p->pw_name != NULL) {
realname = p->pw_name;
mr = _sql_dispatch(_sql_make_cmd(cmd->tmp_pool, 2, "default", realname),
"sql_escapestring" );
_sql_check_response(mr);
username = (char *) mr->data;
usrwhere = pstrcat(cmd->tmp_pool, cmap.usrfield, "='", username, "'", NULL);
sql_log(DEBUG_WARN, "cache miss for user '%s'", realname);
} else {
/* Assume we have a uid */
snprintf(uidstr, MOD_SQL_BUFSIZE, "%lu", (unsigned long) p->pw_uid);
sql_log(DEBUG_WARN, "cache miss for uid '%s'", uidstr);
if (cmap.uidfield)
usrwhere = pstrcat(cmd->tmp_pool, cmap.uidfield, " = ", uidstr, NULL);
else {
sql_log(DEBUG_WARN, "no user uid field configured, declining to "
"lookup uid '%s'", uidstr);
/* If no uid field has been configured, return now and let other
* modules possibly have a chance at resolving this UID to a name.
*/
return NULL;
}
}
if (!cmap.usercustom) {
where = _sql_where(cmd->tmp_pool, 2, usrwhere, cmap.userwhere );
mr = _sql_dispatch(_sql_make_cmd(cmd->tmp_pool, 5, "default",
cmap.usrtable, cmap.usrfields, where, "1"), "sql_select");
_sql_check_response(mr);
if (MODRET_HASDATA(mr))
sd = (sql_data_t *) mr->data;
} else {
mr = sql_lookup(_sql_make_cmd(cmd->tmp_pool, 3, "default", cmap.usercustom,
realname ? realname : "NULL"));
_sql_check_response(mr);
if (MODRET_HASDATA(mr)) {
array_header *ah = (array_header *) mr->data;
sd = pcalloc(cmd->tmp_pool, sizeof(sql_data_t));
/* Assume the query only returned 1 row. */
sd->fnum = ah->nelts;
if (sd->fnum) {
sd->rnum = 1;
sd->data = (char **) ah->elts;
} else {
sd->rnum = 0;
sd->data = NULL;
}
}
}
/* if we have no data.. */
if (sd == NULL ||
sd->rnum == 0) {
if (!cmap.negative_cache) {
return NULL;
} else {
/* If doing caching of negative lookups, cache this failed lookup.
* Use the default UID and GID.
*/
return _sql_addpasswd(cmd, username, NULL, p->pw_uid, p->pw_gid,
NULL, NULL);
}
}
i = 0;
username = sd->data[i++];
password = sd->data[i++];
uid = cmap.defaultuid;
if (cmap.uidfield) {
if (sd->data[i]) {
uid = atoi(sd->data[i++]);
} else {
i++;
}
}
gid = cmap.defaultgid;
if (cmap.gidfield) {
if (sd->data[i]) {
gid = atoi(sd->data[i++]);
} else {
i++;
}
}
dir = cmap.defaulthomedir;
if (sd->data[i]) {
if (strcmp(sd->data[i], "") == 0 ||
strcmp(sd->data[i], "NULL") == 0)
/* Leave dir pointing to the SQLDefaultHomedir, if any. */
i++;
else
dir = sd->data[i++];
}
if (cmap.shellfield) {
if (sd->fnum < i || !sd->data[i]) {
/* Make sure that, if configured, the shell value is valid, and scream
* if it is not.
*/
sql_log(DEBUG_WARN, "NULL shell column value, setting to \"\"");
shell = "";
} else {
shell = sd->data[i];
}
} else
shell = "";
if (uid < cmap.minuseruid)
uid = cmap.defaultuid;
if (gid < cmap.minusergid)
gid = cmap.defaultgid;
return _sql_addpasswd(cmd, username, password, uid, gid, shell, dir);
}
/* _sql_addgroup: creates a group and adds it to the group struct
* cache if it doesn't already exist. Returns the created group
* struct, or the pre-existing struct if there was one.
*
* DOES NOT CHECK ARGUMENTS. CALLING FUNCTIONS NEED TO MAKE SURE
* THEY PASS VALID DATA
*/
static struct group *_sql_addgroup(cmd_rec *cmd, char *groupname, gid_t gid,
array_header *ah) {
struct group *cached = NULL;
struct group *grp = NULL;
int cnt = 0;
grp = pcalloc(cmd->tmp_pool, sizeof(struct group));
grp->gr_gid = gid;
grp->gr_name = groupname;
/* check to make sure the entry doesn't exist in the cache */
if ((cached = (struct group *) cache_findvalue(group_name_cache, grp)) != NULL) {
grp = cached;
sql_log(DEBUG_INFO, "cache hit for group '%s'", grp->gr_name);
} else {
grp = pcalloc(sql_pool, sizeof(struct group));
if (groupname)
grp->gr_name = pstrdup(sql_pool, groupname);
grp->gr_gid = gid;
/* finish filling in the group */
grp->gr_mem = (char **) pcalloc(sql_pool, sizeof(char *) * (ah->nelts + 1));
for (cnt = 0; cnt < ah->nelts; cnt++) {
grp->gr_mem[cnt] = pstrdup(sql_pool, ((char **) ah->elts)[cnt]);
}
grp->gr_mem[ah->nelts] = '\0';
cache_addentry(group_name_cache, grp);
cache_addentry(group_gid_cache, grp);
sql_log(DEBUG_INFO, "cache miss for group '%s'", grp->gr_name);
sql_log(DEBUG_INFO, "group '%s' cached", grp->gr_name);
show_group(cmd->tmp_pool, grp);
}
return grp;
}
static struct group *_sql_getgroup(cmd_rec *cmd, struct group *g) {
struct group *grp = NULL;
modret_t *mr = NULL;
int cnt = 0;
sql_data_t *sd = NULL;
char *groupname = NULL;
char gidstr[MOD_SQL_BUFSIZE] = {'\0'};
char **rows = NULL;
int numrows = 0;
array_header *ah = NULL;
char *members = NULL;
char *member = NULL;
char *grpwhere;
char *where;
char *iterator;
gid_t gid = 0;
if (g == NULL) {
sql_log(DEBUG_WARN, "%s", "_sql_getgroup called with NULL group struct");
sql_log(DEBUG_WARN, "%s", "THIS SHOULD NEVER HAPPEN");
return NULL;
}
/* check to see if the group already exists in one of the group caches */
if (((grp = (struct group *) cache_findvalue(group_name_cache, g)) != NULL) ||
((grp = (struct group *) cache_findvalue(group_gid_cache, g)) != NULL)) {
sql_log(DEBUG_AUTH, "cache hit for group '%s'", grp->gr_name);
/* Check for negatively cached groups, which will have NULL gr_mem. */
if (!grp->gr_mem) {
sql_log(DEBUG_AUTH, "negative cache entry for group '%s'", grp->gr_name);
return NULL;
}
return grp;
}
if (g->gr_name != NULL) {
groupname = g->gr_name;
sql_log(DEBUG_WARN, "cache miss for group '%s'", groupname);
} else {
/* Get groupname from gid */
snprintf(gidstr, MOD_SQL_BUFSIZE, "%lu", (unsigned long) g->gr_gid);
sql_log(DEBUG_WARN, "cache miss for gid '%s'", gidstr);
if (cmap.grpgidfield)
grpwhere = pstrcat(cmd->tmp_pool, cmap.grpgidfield, " = ", gidstr, NULL);
else {
sql_log(DEBUG_WARN, "no group gid field configured, declining to lookup "
"gid '%s'", gidstr);
/* If no gid field has been configured, return now and let other
* modules possibly have a chance at resolving this GID to a name.
*/
return NULL;
}
where = _sql_where(cmd->tmp_pool, 2, grpwhere, cmap.groupwhere);
mr = _sql_dispatch(_sql_make_cmd(cmd->tmp_pool, 5, "default",
cmap.grptable, cmap.grpfield, where, "1"), "sql_select");
_sql_check_response(mr);
sd = (sql_data_t *) mr->data;
/* If we have no data.. */
if (sd->rnum == 0)
return NULL;
groupname = sd->data[0];
}
grpwhere = pstrcat(cmd->tmp_pool, cmap.grpfield, " = '", groupname, "'",
NULL);
where = _sql_where(cmd->tmp_pool, 2, grpwhere, cmap.groupwhere);
mr = _sql_dispatch(_sql_make_cmd(cmd->tmp_pool, 4, "default",
cmap.grptable, cmap.grpfields, where), "sql_select");
_sql_check_response(mr);
sd = (sql_data_t *) mr->data;
/* if we have no data.. */
if (sd->rnum == 0) {
if (!cmap.negative_cache) {
return NULL;
} else {
/* If doing caching of negative lookups, cache this failed lookup. */
return _sql_addgroup(cmd, groupname, g->gr_gid, NULL);
}
}
rows = sd->data;
numrows = sd->rnum;
gid = (gid_t) strtoul(rows[1], NULL, 10);
/*
* painful.. we need to walk through the returned rows and fill in our
* members. Every third element in a row is a member field, and every
* member field can have multiple members.
*/
ah = make_array(cmd->tmp_pool, 10, sizeof(char *));
for (cnt = 0; cnt < numrows; cnt++) {
members = rows[(cnt * 3) + 2];
iterator = members;
/* If the row is null, continue.. */
if (members == NULL)
continue;
/* For each member in the list, toss 'em into the array. no
* need to copy the string -- _sql_addgroup will do it for us
*/
for (member = strsep(&iterator, ","); member;
member = strsep(&iterator, ",")) {
if (*member == '\0')
continue;
*((char **) push_array(ah)) = member;
}
}
return _sql_addgroup(cmd, groupname, gid, ah);
}
static void _setstats(cmd_rec *cmd, int fstor, int fretr, int bstor,
int bretr) {
/*
* if anyone has a better way of doing this, let me know..
*/
char query[256] = { '\0' };
char *usrwhere, *where;
modret_t *mr = NULL;
snprintf(query, sizeof(query),
"%s = %s + %i, %s = %s + %i, %s = %s + %i, %s = %s + %i",
cmap.sql_fstor, cmap.sql_fstor, fstor,
cmap.sql_fretr, cmap.sql_fretr, fretr,
cmap.sql_bstor, cmap.sql_bstor, bstor,
cmap.sql_bretr, cmap.sql_bretr, bretr);
usrwhere = pstrcat(cmd->tmp_pool, cmap.usrfield, " = '", _sql_realuser(cmd), "'", NULL);
where = _sql_where(cmd->tmp_pool, 2, usrwhere, cmap.userwhere );
mr = _sql_dispatch( _sql_make_cmd( cmd->tmp_pool, 4, "default", cmap.usrtable,
query, where ), "sql_update" );
_sql_check_response(mr);
}
static int _sql_getgroups(cmd_rec *cmd) {
struct passwd *pw = NULL, lpw;
struct group *grp, lgr;
char *grpwhere = NULL, *where = NULL, **rows = NULL;
sql_data_t *sd = NULL;
modret_t *mr = NULL;
array_header *gids = NULL, *groups = NULL;
char *name = cmd->argv[0], *username = NULL;
int numrows = 0;
register unsigned int i = 0;
/* Check for NULL values */
if (cmd->argv[1])
gids = (array_header *) cmd->argv[1];
if (cmd->argv[2])
groups = (array_header *) cmd->argv[2];
lpw.pw_uid = -1;
lpw.pw_name = name;
/* Retrieve the necessary info */
if (!name ||
!(pw = _sql_getpasswd(cmd, &lpw)))
return -1;
/* Populate the first group ID and name */
if (gids)
*((gid_t *) push_array(gids)) = pw->pw_gid;
lgr.gr_gid = pw->pw_gid;
lgr.gr_name = NULL;
if (groups &&
(grp = _sql_getgroup(cmd, &lgr)) != NULL)
*((char **) push_array(groups)) = pstrdup(permanent_pool, grp->gr_name);
/* Use a single SELECT:
*
* SELECT groupname,gid,members FROM groups
* WHERE members LIKE '%,<user>,%' OR LIKE '<user>,%' OR LIKE '%,<user>';
*/
mr = _sql_dispatch(_sql_make_cmd(cmd->tmp_pool, 2, "default", name),
"sql_escapestring");
_sql_check_response(mr);
username = (char *) mr->data;
grpwhere = pstrcat(cmd->tmp_pool,
cmap.grpmembersfield, " = '", username, "' OR ",
cmap.grpmembersfield, " LIKE '", username, ",%' OR ",
cmap.grpmembersfield, " LIKE '%,", username, "' OR ",
cmap.grpmembersfield, " LIKE '%,", username, ",%'", NULL);
where = _sql_where(cmd->tmp_pool, 2, grpwhere, cmap.groupwhere);
mr = _sql_dispatch(_sql_make_cmd(cmd->tmp_pool, 4, "default",
cmap.grptable, cmap.grpfields, where), "sql_select");
_sql_check_response(mr);
sd = (sql_data_t *) mr->data;
/* If we have no data... */
if (sd->rnum == 0)
return -1;
rows = sd->data;
numrows = sd->rnum;
for (i = 0; i < numrows; i++) {
char *groupname = sd->data[(i * 3)];
gid_t gid = (gid_t) atoi(sd->data[(i * 3) +1]);
char *memberstr = sd->data[(i * 3) + 2], *member = NULL;
array_header *members = make_array(cmd->tmp_pool, 2, sizeof(char *));
*((gid_t *) push_array(gids)) = gid;
*((char **) push_array(groups)) = pstrdup(permanent_pool, groupname);
/* For each member in the list, toss 'em into the array. no
* need to copy the string -- _sql_addgroup will do it for us
*/
for (member = strsep(&memberstr, ","); member;
member = strsep(&memberstr, ",")) {
if (*member == '\0')
continue;
*((char **) push_array(members)) = member;
}
/* Add this group data to the group cache. */
_sql_addgroup(cmd, groupname, gid, members);
}
if (gids && gids->nelts > 0)
return gids->nelts;
else if (groups && groups->nelts)
return groups->nelts;
/* Default */
return -1;
}
/* Command handlers
*/
MODRET sql_pre_pass(cmd_rec *cmd) {
config_rec *c = NULL, *anon_config = NULL;
char *user = NULL;
if (cmap.engine == 0)
return DECLINED(cmd);
sql_log(DEBUG_FUNC, "%s", ">>> sql_pre_pass");
user = get_param_ptr(cmd->server->conf, C_USER, FALSE);
if (!user) {
sql_log(DEBUG_FUNC, "%s", "Missing user name, skipping");
sql_log(DEBUG_FUNC, "%s", "<<< sql_pre_pass");
return DECLINED(cmd);
}
/* Use the looked-up user name to determine whether this is to be
* an anonymous session.
*/
anon_config = pr_auth_get_anon_config(cmd->pool, &user, NULL, NULL);
c = find_config(anon_config ? anon_config->subset : main_server->conf,
CONF_PARAM, "SQLEngine", FALSE);
if (c)
cmap.engine = *((int *) c->argv[0]);
sql_log(DEBUG_FUNC, "%s", "<<< sql_pre_pass");
return DECLINED(cmd);
}
MODRET sql_post_stor(cmd_rec *cmd) {
if (cmap.engine == 0)
return DECLINED(cmd);
sql_log(DEBUG_FUNC, "%s", ">>> sql_post_stor");
if (cmap.sql_fstor)
_setstats(cmd, 1, 0, session.xfer.total_bytes, 0);
sql_log(DEBUG_FUNC, "%s", "<<< sql_post_stor");
return DECLINED(cmd);
}
MODRET sql_post_retr(cmd_rec *cmd) {
if (cmap.engine == 0)
return DECLINED(cmd);
sql_log(DEBUG_FUNC, "%s", ">>> sql_post_retr");
if (cmap.sql_fretr)
_setstats(cmd, 0, 1, 0, session.xfer.total_bytes);
sql_log(DEBUG_FUNC, "%s", "<<< sql_post_retr");
return DECLINED(cmd);
}
static char *resolve_tag(cmd_rec *cmd, char tag) {
char arg[256] = {'\0'}, *argp;
switch(tag) {
case 'A': {
char *pass;
argp = arg;
pass = get_param_ptr(main_server->conf, C_PASS, FALSE);
if (!pass)
pass = "UNKNOWN";
sstrncpy( argp, pass, sizeof(arg));
}
break;
case 'a':
argp = arg;
sstrncpy(argp, pr_netaddr_get_ipstr(pr_netaddr_get_sess_remote_addr()),
sizeof(arg));
break;
case 'b':
argp = arg;
if (session.xfer.p)
snprintf(argp, sizeof(arg), "%" PR_LU, session.xfer.total_bytes);
else
sstrncpy( argp, "0", sizeof(arg));
break;
case 'c':
argp = arg;
sstrncpy(argp, session.class ? session.class->cls_name : "-", sizeof(arg));
break;
case 'd':
argp = arg;
if (strcmp(cmd->argv[0], C_CDUP) == 0 ||
strcmp(cmd->argv[0], C_CWD) == 0 ||
strcmp(cmd->argv[0], C_MKD) == 0 ||
strcmp(cmd->argv[0], C_RMD) == 0 ||
strcmp(cmd->argv[0], C_XCWD) == 0 ||
strcmp(cmd->argv[0], C_XCUP) == 0 ||
strcmp(cmd->argv[0], C_XMKD) == 0 ||
strcmp(cmd->argv[0], C_XRMD) == 0) {
char *tmp = strrchr(cmd->arg, '/');
sstrncpy(argp, tmp ? tmp : cmd->arg, sizeof(arg));
} else
sstrncpy(argp, "", sizeof(arg));
break;
case 'D':
argp = arg;
if (strcmp(cmd->argv[0], C_CDUP) == 0 ||
strcmp(cmd->argv[0], C_MKD) == 0 ||
strcmp(cmd->argv[0], C_RMD) == 0 ||
strcmp(cmd->argv[0], C_XCUP) == 0 ||
strcmp(cmd->argv[0], C_XMKD) == 0 ||
strcmp(cmd->argv[0], C_XRMD) == 0) {
sstrncpy(argp, dir_abs_path(cmd->tmp_pool, cmd->arg, TRUE), sizeof(arg));
} else if (strcmp(cmd->argv[0], C_CWD) == 0 ||
strcmp(cmd->argv[0], C_XCWD) == 0) {
/* Note: by this point in the dispatch cycle, the current working
* directory has already been changed. For the CWD/XCWD commands,
* this means that dir_abs_path() may return an improper path,
* with the target directory being reported twice. To deal with this,
* don't use dir_abs_path(), and use pr_fs_getvwd()/pr_fs_getcwd()
* instead.
*/
if (session.chroot_path) {
/* Chrooted session. */
sstrncpy(arg, strcmp(pr_fs_getvwd(), "/") ?
pdircat(cmd->tmp_pool, session.chroot_path, pr_fs_getvwd(), NULL) :
session.chroot_path, sizeof(arg));
} else
/* Non-chrooted session. */
sstrncpy(arg, pr_fs_getcwd(), sizeof(arg));
} else
sstrncpy(argp, "", sizeof(arg));
break;
case 'f':
argp = arg;
if (strcmp(cmd->argv[0], C_RNTO) == 0) {
sstrncpy(argp, dir_abs_path(cmd->tmp_pool, cmd->arg, TRUE), sizeof(arg));
} else if (session.xfer.p &&
session.xfer.path) {
sstrncpy(argp, dir_abs_path(cmd->tmp_pool, session.xfer.path, TRUE),
sizeof(arg));
} else {
/* Some commands (i.e. DELE, MKD, RMD, XMKD, and XRMD) have associated
* filenames that are not stored in the session.xfer structure; these
* should be expanded properly as well.
*/
if (strcmp(cmd->argv[0], C_DELE) == 0 ||
strcmp(cmd->argv[0], C_MKD) == 0 ||
strcmp(cmd->argv[0], C_RMD) == 0 ||
strcmp(cmd->argv[0], C_XMKD) == 0 ||
strcmp(cmd->argv[0], C_XRMD) == 0)
sstrncpy(arg, dir_abs_path(cmd->tmp_pool, cmd->arg, TRUE), sizeof(arg));
else
/* All other situations get a "-". */
sstrncpy(argp, "-", sizeof(arg));
}
break;
case 'F':
argp = arg;
if (session.xfer.p && session.xfer.path) {
sstrncpy(argp, session.xfer.path, sizeof(arg));
} else {
/* Some commands (i.e. DELE) have associated filenames that are not
* stored in the session.xfer structure; these should be expanded
* properly as well.
*/
if (strcmp(cmd->argv[0], C_DELE) == 0)
sstrncpy(arg, cmd->arg, sizeof(arg));
else
sstrncpy(argp, "-", sizeof(arg));
}
break;
case 'J':
argp = arg;
if (strcasecmp(cmd->argv[0], C_PASS) == 0 &&
session.hide_password) {
sstrncpy(argp, "(hidden)", sizeof(arg));
} else {
sstrncpy(argp, cmd->arg, sizeof(arg));
}
break;
case 'h':
argp = arg;
sstrncpy(argp, pr_netaddr_get_sess_remote_name(), sizeof(arg));
break;
case 'L':
argp = arg;
sstrncpy(argp, pr_netaddr_get_ipstr(pr_netaddr_get_sess_local_addr()),
sizeof(arg));
break;
case 'l':
argp = arg;
sstrncpy(argp, session.ident_user, sizeof(arg));
break;
case 'm':
argp = arg;
sstrncpy(argp, cmd->argv[0], sizeof(arg));
break;
case 'P':
argp = arg;
snprintf(argp, sizeof(arg), "%u",(unsigned int)getpid());
break;
case 'p':
argp = arg;
snprintf(argp, sizeof(arg), "%d", cmd->server->ServerPort);
break;
case 'r':
argp = arg;
if(!strcasecmp(cmd->argv[0], C_PASS) && session.hide_password)
sstrncpy(argp, C_PASS " (hidden)", sizeof(arg));
else
sstrncpy(argp, get_full_cmd(cmd), sizeof(arg));
break;
case 's': {
pr_response_t *r;
argp = arg;
r = (resp_list ? resp_list : resp_err_list);
for (; r && !r->num; r=r->next);
if (r && r->num)
sstrncpy(argp, r->num, sizeof(arg));
else
sstrncpy(argp, "-", sizeof(arg));
}
break;
case 'T':
argp = arg;
if (session.xfer.p) {
struct timeval end_time;
gettimeofday(&end_time, NULL);
end_time.tv_sec -= session.xfer.start_time.tv_sec;
if (end_time.tv_usec >= session.xfer.start_time.tv_usec)
end_time.tv_usec -= session.xfer.start_time.tv_usec;
else {
end_time.tv_usec = 1000000L - (session.xfer.start_time.tv_usec -
end_time.tv_usec);
end_time.tv_sec--;
}
snprintf(argp, sizeof(arg), "%lu.%03lu", (unsigned long) end_time.tv_sec,
(unsigned long) (end_time.tv_usec / 1000));
} else
sstrncpy(argp, "0.0", sizeof(arg));
break;
case 'U':
argp = arg;
{
char *login_user = get_param_ptr(main_server->conf, C_USER, FALSE);
if (!login_user)
login_user = "root";
sstrncpy(argp, login_user, sizeof(arg));
}
break;
case 'u':
argp = arg;
if (!session.user) {
char *u;
u = get_param_ptr(main_server->conf, "UserName", FALSE);
if (!u)
u = "root";
sstrncpy(argp, u, sizeof(arg));
} else
sstrncpy(argp, session.user, sizeof(arg));
break;
case 'V':
argp = arg;
sstrncpy(argp, cmd->server->ServerFQDN, sizeof(arg));
break;
case 'v':
argp = arg;
sstrncpy(argp, cmd->server->ServerName, sizeof(arg));
break;
case '%':
argp = "%";
break;
default:
argp = "{UNKNOWN TAG}";
break;
}
return pstrdup(cmd->tmp_pool, argp);
}
static char *_named_query_type(cmd_rec *cmd, char *name) {
config_rec *c = NULL;
char *query = NULL;
query = pstrcat(cmd->tmp_pool, "SQLNamedQuery_", name, NULL);
c = find_config(main_server->conf, CONF_PARAM, query, FALSE);
if (c)
return c->argv[0];
return NULL;
}
static modret_t *_process_named_query(cmd_rec *cmd, char *name) {
config_rec *c;
char *query, *tmp, *argp;
char outs[4096] = {'\0'}, *outsp;
char *esc_arg = NULL;
modret_t *mr = NULL;
int num = 0;
char *argc = 0;
char *endptr = NULL;
sql_log(DEBUG_FUNC, "%s", ">>> _process_named_query");
/* Check for a query by that name */
query = pstrcat(cmd->tmp_pool, "SQLNamedQuery_", name, NULL);
c = find_config(main_server->conf, CONF_PARAM, query, FALSE);
if (c) {
/* Select string fixup */
memset(outs, '\0', sizeof(outs));
outsp = outs;
for (tmp = c->argv[1]; *tmp; ) {
if (*tmp == '%') {
if (*(++tmp) == '{') {
char *tmp_query;
if (*tmp != '\0')
tmp_query = ++tmp;
/* Find the argument number to use */
while (*tmp && *tmp != '}')
tmp++;
argc = pstrndup(cmd->tmp_pool, tmp_query, (tmp - tmp_query));
if (argc) {
num = strtol(argc, &endptr, 10);
if (*endptr != '\0' ||
num < 0 ||
(cmd->argc - 3) < num) {
return ERROR_MSG(cmd, MOD_SQL_VERSION,
"reference out-of-bounds in query");
}
} else {
return ERROR_MSG(cmd, MOD_SQL_VERSION,
"malformed reference %{?} in query");
}
esc_arg = cmd->argv[num+2];
} else {
argp = resolve_tag(cmd, *tmp);
mr = _sql_dispatch(_sql_make_cmd(cmd->tmp_pool, 2, "default",
argp), "sql_escapestring");
_sql_check_response(mr);
esc_arg = (char *) mr->data;
}
/* XXX Should be sstrcat(). */
strcat(outs, esc_arg);
outsp += strlen(esc_arg);
if (*tmp != '\0')
tmp++;
} else {
*outsp++ = *tmp++;
}
}
*outsp++ = 0;
/* Construct our return data based on the type of query */
if (strcasecmp(c->argv[0], SQL_UPDATE_C) == 0) {
query = pstrcat(cmd->tmp_pool, c->argv[2], " SET ", outs, NULL);
mr = _sql_dispatch(_sql_make_cmd(cmd->tmp_pool, 2, "default", query),
"sql_update");
} else if (strcasecmp(c->argv[0], SQL_INSERT_C) == 0) {
query = pstrcat(cmd->tmp_pool, "INTO ", c->argv[2], " VALUES (",
outs, ")", NULL);
mr = _sql_dispatch(_sql_make_cmd(cmd->tmp_pool, 2, "default", query),
"sql_insert");
} else if (strcasecmp(c->argv[0], SQL_FREEFORM_C) == 0) {
mr = _sql_dispatch(_sql_make_cmd(cmd->tmp_pool, 2, "default", outs),
"sql_query");
} else if (strcasecmp(c->argv[0], SQL_SELECT_C) == 0) {
mr = _sql_dispatch(_sql_make_cmd(cmd->tmp_pool, 2, "default", outs),
"sql_select");
} else {
mr = ERROR_MSG(cmd, MOD_SQL_VERSION, "unknown NamedQuery type");
}
} else {
mr = ERROR(cmd);
}
sql_log(DEBUG_FUNC, "%s", "<<< _process_named_query");
return mr;
}
MODRET log_master(cmd_rec *cmd) {
char *name = NULL;
char *qname = NULL;
char *type = NULL;
config_rec *c = NULL;
modret_t *mr = NULL;
if (!(cmap.engine & SQL_ENGINE_FL_LOG))
return DECLINED(cmd);
/* handle explicit queries */
name = pstrcat(cmd->tmp_pool, "SQLLog_", cmd->argv[0], NULL);
c = find_config(main_server->conf, CONF_PARAM, name, FALSE);
if (c) {
do {
sql_log(DEBUG_FUNC, "%s", ">>> log_master");
qname = c->argv[0];
type = _named_query_type(cmd, qname);
if (type) {
if ((!strcasecmp(type, SQL_UPDATE_C)) ||
(!strcasecmp(type, SQL_FREEFORM_C)) ||
(!strcasecmp(type, SQL_INSERT_C))) {
mr = _process_named_query( cmd, qname );
if (c->argc == 2) _sql_check_response(mr);
} else {
sql_log(DEBUG_WARN, "named query '%s' is not an INSERT, UPDATE, or "
"FREEFORM query", qname);
}
} else {
sql_log(DEBUG_WARN, "named query '%s' cannot be found", qname);
}
sql_log(DEBUG_FUNC, "%s", "<<< log_master");
} while((c = find_config_next(c, c->next,
CONF_PARAM, name, FALSE)) != NULL);
}
/* handle implit queries */
name = pstrcat(cmd->tmp_pool, "SQLLog_*", NULL);
c = find_config(main_server->conf, CONF_PARAM, name, FALSE);
if (c) {
do {
sql_log(DEBUG_FUNC, "%s", ">>> log_master");
qname = c->argv[0];
type = _named_query_type(cmd, qname);
if (type) {
if ((!strcasecmp(type, SQL_UPDATE_C)) ||
(!strcasecmp(type, SQL_FREEFORM_C)) ||
(!strcasecmp(type, SQL_INSERT_C))) {
mr = _process_named_query( cmd, qname );
if (c->argc == 2) _sql_check_response(mr);
} else {
sql_log(DEBUG_WARN, "named query '%s' is not an INSERT, UPDATE, or "
"FREEFORM query", qname);
}
} else {
sql_log(DEBUG_WARN, "named query '%s' cannot be found", qname);
}
sql_log(DEBUG_FUNC, "%s", "<<< log_master");
} while((c = find_config_next(c, c->next,
CONF_PARAM, name, FALSE)) != NULL);
}
return DECLINED(cmd);
}
MODRET err_master(cmd_rec *cmd) {
char *name = NULL;
char *qname = NULL;
char *type = NULL;
config_rec *c = NULL;
modret_t *mr = NULL;
if (!(cmap.engine & SQL_ENGINE_FL_LOG))
return DECLINED(cmd);
/* handle explicit errors */
name = pstrcat(cmd->tmp_pool, "SQLLog_ERR_", cmd->argv[0], NULL);
c = find_config(main_server->conf, CONF_PARAM, name, FALSE);
if (c) {
do {
sql_log(DEBUG_FUNC, "%s", ">>> err_master");
qname = c->argv[0];
type = _named_query_type(cmd, qname);
if (type) {
if ((!strcasecmp(type, SQL_UPDATE_C)) ||
(!strcasecmp(type, SQL_FREEFORM_C)) ||
(!strcasecmp(type, SQL_INSERT_C))) {
mr = _process_named_query( cmd, qname );
if (c->argc == 2) _sql_check_response(mr);
} else {
sql_log(DEBUG_WARN, "named query '%s' is not an INSERT, UPDATE, or "
"FREEFORM query", qname);
}
} else {
sql_log(DEBUG_WARN, "named query '%s' cannot be found", qname);
}
sql_log(DEBUG_FUNC, "%s", "<<< err_master");
} while((c = find_config_next(c, c->next,
CONF_PARAM, name, FALSE)) != NULL);
}
/* handle implicit errors */
name = pstrcat(cmd->tmp_pool, "SQLLog_ERR_*", NULL);
c = find_config(main_server->conf, CONF_PARAM, name, FALSE);
if (c) {
do {
sql_log(DEBUG_FUNC, "%s", ">>> err_master");
qname = c->argv[0];
type = _named_query_type(cmd, qname);
if (type) {
if ((!strcasecmp(type, SQL_UPDATE_C)) ||
(!strcasecmp(type, SQL_FREEFORM_C)) ||
(!strcasecmp(type, SQL_INSERT_C))) {
mr = _process_named_query( cmd, qname );
if (c->argc == 2) _sql_check_response(mr);
} else {
sql_log(DEBUG_WARN, "named query '%s' is not an INSERT, UPDATE, or "
"FREEFORM query", qname);
}
} else {
sql_log(DEBUG_WARN, "named query '%s' cannot be found", qname);
}
sql_log(DEBUG_FUNC, "%s", "<<< err_master");
} while((c = find_config_next(c, c->next,
CONF_PARAM, name, FALSE)) != NULL);
}
return DECLINED(cmd);
}
MODRET info_master(cmd_rec *cmd) {
char *type = NULL;
char *name = NULL;
config_rec *c = NULL;
char outs[4096] = {'\0'}, *outsp;
char *argp = NULL;
char *tmp = NULL;
modret_t *mr = NULL;
sql_data_t *sd = NULL;
if (!(cmap.engine & SQL_ENGINE_FL_LOG))
return DECLINED(cmd);
/* process explicit handlers */
name = pstrcat(cmd->tmp_pool, "SQLShowInfo_", cmd->argv[0], NULL);
c = find_config(main_server->conf, CONF_PARAM, name, FALSE);
if (c) {
sql_log(DEBUG_FUNC, "%s", ">>> info_master");
/* we now have at least one config_rec. Take the output string from
* each, and process it -- resolve tags, and when we find a named
* query, run it and get info from it.
*/
do {
memset(outs, '\0', sizeof(outs));
outsp = outs;
for (tmp = c->argv[1]; *tmp; ) {
if(*tmp == '%') {
/* is the tag a named_query reference? If so, process the
* named query, otherwise process it as a normal tag..
*/
if (*(++tmp) == '{') {
char *query;
if (*tmp!='\0') query = ++tmp;
/* get the name of the query */
while ( *tmp && *tmp!='}' ) tmp++;
query = pstrndup(cmd->tmp_pool, query, (tmp - query));
/* make sure it's a SELECT query */
type = _named_query_type(cmd, query);
if (type && ((!strcasecmp(type, SQL_SELECT_C )) ||
(!strcasecmp(type, SQL_FREEFORM_C )))) {
mr = _process_named_query(cmd, query);
if (MODRET_ISERROR(mr)) {
argp = "{null}";
} else {
sd = (sql_data_t *) mr->data;
if ((sd->rnum == 0) || (!sd->data[0]))
argp = "{null}";
else
argp = sd->data[0];
}
} else {
argp = "{null}";
}
} else {
argp=resolve_tag( cmd, *tmp);
}
sstrcat(outs, argp, sizeof(outs));
outsp += strlen(argp);
if (*tmp!='\0') tmp++;
} else {
*outsp++ = *tmp++;
}
}
*outsp++ = 0;
/* add the response */
pr_response_add(c->argv[0], "%s", outs);
} while((c = find_config_next(c, c->next, CONF_PARAM, name, FALSE)) != NULL);
sql_log(DEBUG_FUNC, "%s", "<<< info_master");
}
/* process implicit handlers */
name = pstrcat(cmd->tmp_pool, "SQLShowInfo_*", NULL);
c = find_config(main_server->conf, CONF_PARAM, name, FALSE);
if (c) {
sql_log(DEBUG_FUNC, "%s", ">>> info_master");
/* we now have at least one config_rec. Take the output string from
* each, and process it -- resolve tags, and when we find a named
* query, run it and get info from it.
*/
do {
memset(outs, '\0', sizeof(outs));
outsp = outs;
for (tmp = c->argv[1]; *tmp; ) {
if(*tmp == '%') {
/* is the tag a named_query reference? If so, process the
* named query, otherwise process it as a normal tag..
*/
if (*(++tmp) == '{') {
char *query;
if (*tmp!='\0') query = ++tmp;
/* get the name of the query */
while ( *tmp && *tmp!='}' ) tmp++;
query = pstrndup(cmd->tmp_pool, query, (tmp - query));
/* make sure it's a SELECT query */
type = _named_query_type(cmd, query);
if (type && ((!strcasecmp(type, SQL_SELECT_C )) ||
(!strcasecmp(type, SQL_FREEFORM_C )))) {
mr = _process_named_query(cmd, query);
if (MODRET_ISERROR(mr)) {
argp = "{null}";
} else {
sd = (sql_data_t *) mr->data;
if ((sd->rnum == 0) || (!sd->data[0]))
argp = "{null}";
else
argp = sd->data[0];
}
} else {
argp = "{null}";
}
} else {
argp=resolve_tag( cmd, *tmp);
}
sstrcat(outs, argp, sizeof(outs));
outsp += strlen(argp);
if (*tmp!='\0') tmp++;
} else {
*outsp++ = *tmp++;
}
}
*outsp++ = 0;
/* add the response */
pr_response_add(c->argv[0], "%s", outs);
} while((c = find_config_next(c, c->next, CONF_PARAM, name, FALSE)) != NULL);
sql_log(DEBUG_FUNC, "%s", "<<< info_master");
}
return DECLINED(cmd);
}
MODRET errinfo_master(cmd_rec *cmd) {
char *type = NULL;
char *name = NULL;
config_rec *c = NULL;
char outs[4096] = {'\0'}, *outsp;
char *argp = NULL;
char *tmp = NULL;
modret_t *mr = NULL;
sql_data_t *sd = NULL;
if (!(cmap.engine & SQL_ENGINE_FL_LOG))
return DECLINED(cmd);
/* process explicit handlers */
name = pstrcat(cmd->tmp_pool, "SQLShowInfo_ERR_", cmd->argv[0], NULL);
c = find_config(main_server->conf, CONF_PARAM, name, FALSE);
if (c) {
sql_log(DEBUG_FUNC, "%s", ">>> errinfo_master");
/* we now have at least one config_rec. Take the output string from
* each, and process it -- resolve tags, and when we find a named
* query, run it and get info from it.
*/
do {
memset(outs, '\0', sizeof(outs));
outsp = outs;
for (tmp = c->argv[1]; *tmp; ) {
if(*tmp == '%') {
/* is the tag a named_query reference? If so, process the
* named query, otherwise process it as a normal tag..
*/
if (*(++tmp) == '{') {
char *query;
if (*tmp!='\0') query = ++tmp;
/* get the name of the query */
while ( *tmp && *tmp!='}' ) tmp++;
query = pstrndup(cmd->tmp_pool, query, (tmp - query));
/* make sure it's a SELECT query */
type = _named_query_type(cmd, query);
if (type && ((!strcasecmp(type, SQL_SELECT_C )) ||
(!strcasecmp(type, SQL_FREEFORM_C )))) {
mr = _process_named_query(cmd, query);
if (MODRET_ISERROR(mr)) {
argp = "{null}";
} else {
sd = (sql_data_t *) mr->data;
if ((sd->rnum == 0) || (!sd->data[0]))
argp = "{null}";
else
argp = sd->data[0];
}
} else {
argp = "{null}";
}
} else {
argp=resolve_tag( cmd, *tmp);
}
sstrcat(outs, argp, sizeof(outs));
outsp += strlen(argp);
if (*tmp!='\0') tmp++;
} else {
*outsp++ = *tmp++;
}
}
*outsp++ = 0;
/* add the response */
pr_response_add_err(c->argv[0], "%s", outs);
} while((c = find_config_next(c, c->next, CONF_PARAM, name, FALSE)) != NULL);
sql_log(DEBUG_FUNC, "%s", "<<< errinfo_master");
}
/* process implicit handlers */
name = pstrcat(cmd->tmp_pool, "SQLShowInfo_ERR_*", NULL);
c = find_config(main_server->conf, CONF_PARAM, name, FALSE);
if (c) {
sql_log(DEBUG_FUNC, "%s", ">>> errinfo_master");
/* we now have at least one config_rec. Take the output string from
* each, and process it -- resolve tags, and when we find a named
* query, run it and get info from it.
*/
do {
memset(outs, '\0', sizeof(outs));
outsp = outs;
for (tmp = c->argv[1]; *tmp; ) {
if(*tmp == '%') {
/* is the tag a named_query reference? If so, process the
* named query, otherwise process it as a normal tag..
*/
if (*(++tmp) == '{') {
char *query;
if (*tmp!='\0') query = ++tmp;
/* get the name of the query */
while ( *tmp && *tmp!='}' ) tmp++;
query = pstrndup(cmd->tmp_pool, query, (tmp - query));
/* make sure it's a SELECT query */
type = _named_query_type(cmd, query);
if (type && ((!strcasecmp(type, SQL_SELECT_C )) ||
(!strcasecmp(type, SQL_FREEFORM_C )))) {
mr = _process_named_query(cmd, query);
if (MODRET_ISERROR(mr)) {
argp = "{null}";
} else {
sd = (sql_data_t *) mr->data;
if ((sd->rnum == 0) || (!sd->data[0]))
argp = "{null}";
else
argp = sd->data[0];
}
} else {
argp = "{null}";
}
} else {
argp=resolve_tag( cmd, *tmp);
}
sstrcat(outs, argp, sizeof(outs));
outsp += strlen(argp);
if (*tmp!='\0') tmp++;
} else {
*outsp++ = *tmp++;
}
}
*outsp++ = 0;
/* add the response */
pr_response_add(c->argv[0], "%s", outs);
} while((c = find_config_next(c, c->next, CONF_PARAM, name, FALSE)) != NULL);
sql_log(DEBUG_FUNC, "%s", "<<< errinfo_master");
}
return DECLINED(cmd);
}
/* sql_lookup: used by third-party modules to get data via a SQL query.
* Third party module must pass a legitimate cmd_rec (including tmp_pool),
* and the cmd_rec must have only one argument: the name of a SQLNamedQuery.
*
* Returns:
*
* DECLINED if mod_sql isn't on
* ERROR if named query doesn't exist
*
* SHUTS DOWN if query caused an error
*
* otherwise:
*
* array_header * in the data slot with the returned data. It is up to the
* calling function to know how many pieces of data to expect, and how to
* parse them.
*/
MODRET sql_lookup(cmd_rec *cmd) {
char *type = NULL;
modret_t *mr = NULL;
sql_data_t *sd = NULL;
array_header *ah = NULL;
int cnt = 0;
if (cmap.engine == 0)
return DECLINED(cmd);
sql_log(DEBUG_FUNC, "%s", ">>> sql_lookup");
if (cmd->argc < 1)
return ERROR(cmd);
type = _named_query_type(cmd, cmd->argv[1]);
if (type && ((!strcasecmp(type, SQL_SELECT_C )) ||
(!strcasecmp(type, SQL_FREEFORM_C )))) {
mr = _process_named_query(cmd, cmd->argv[1]);
if (!MODRET_ISERROR(mr)) {
sd = (sql_data_t *) mr->data;
ah = make_array(session.pool, (sd->rnum * sd->fnum) , sizeof(char *));
/* the right way to do this is to preserve the abstraction of the array
* header so things don't blow up when it gets freed */
for (cnt =0; cnt< (sd->rnum * sd->fnum); cnt++) {
*((char **) push_array(ah)) = sd->data[cnt];
}
mr = mod_create_data(cmd, (void *) ah);
} else {
/* we have an error, log it and die */
_sql_check_response(mr);
}
} else {
mr = ERROR(cmd);
}
sql_log(DEBUG_FUNC, "%s", "<<< sql_lookup");
return mr;
}
MODRET sql_change(cmd_rec *cmd) {
char *type = NULL;
modret_t *mr = NULL;
if (cmap.engine == 0)
return DECLINED(cmd);
sql_log(DEBUG_FUNC, "%s", ">>> sql_change");
if (cmd->argc < 1)
return ERROR(cmd);
type = _named_query_type(cmd, cmd->argv[1]);
if (type && ((!strcasecmp(type, SQL_INSERT_C)) ||
(!strcasecmp(type, SQL_UPDATE_C)) ||
(!strcasecmp(type, SQL_FREEFORM_C)))) {
/* fixup the cmd_rec */
mr = _process_named_query(cmd, cmd->argv[1]);
if (MODRET_ISERROR(mr)) {
/* we have an error, log it and die */
_sql_check_response(mr);
}
} else {
mr = ERROR(cmd);
}
sql_log(DEBUG_FUNC, "%s", "<<< sql_change");
return mr;
}
MODRET sql_escapestr(cmd_rec *cmd) {
modret_t *mr;
sql_log(DEBUG_FUNC, "%s", ">>> sql_escapestr");
mr =_sql_dispatch(_sql_make_cmd(cmd->tmp_pool, 2, "default", cmd->argv[0]),
"sql_escapestring");
sql_log(DEBUG_FUNC, "%s", "<<< sql_escapestr");
return mr;
}
/*****************************************************************
*
* AUTH COMMAND HANDLERS
*
*****************************************************************/
MODRET cmd_setpwent(cmd_rec *cmd) {
sql_data_t *sd = NULL;
modret_t *mr = NULL;
char *where = NULL;
int i = 0, cnt = 0;
char *username = NULL;
char *password = NULL;
char *shell = NULL;
char *dir = NULL;
uid_t uid = 0;
gid_t gid = 0;
struct passwd lpw;
if (!SQL_USERSET ||
!(cmap.engine & SQL_ENGINE_FL_AUTH))
return DECLINED(cmd);
sql_log(DEBUG_FUNC, "%s", ">>> cmd_setpwent");
/* if we've already filled the passwd cache, just reset the curr_passwd */
if ( cmap.passwd_cache_filled ) {
cmap.curr_passwd = passwd_name_cache->head;
sql_log(DEBUG_FUNC, "%s", "<<< cmd_setpwent");
return DECLINED(cmd);
}
/* single select or not? */
if (SQL_FASTUSERS) {
/* retrieve our list of passwds */
where = _sql_where(cmd->tmp_pool, 1, cmap.userwhere );
mr = _sql_dispatch( _sql_make_cmd( cmd->tmp_pool, 4, "default",
cmap.usrtable, cmap.usrfields, where ),
"sql_select" );
_sql_check_response(mr);
sd = (sql_data_t *) mr->data;
/* walk through the array, adding users to the cache */
for ( i = 0, cnt = 0; cnt < sd->rnum; cnt++ ) {
username = sd->data[i++];
/* if the username is NULL, skip it */
if ( username == NULL ) continue;
password = sd->data[i++];
uid = cmap.defaultuid;
if (cmap.uidfield) {
if (sd->data[i]) {
uid = atoi(sd->data[i++]);
} else {
i++;
}
}
gid = cmap.defaultgid;
if (cmap.gidfield) {
if (sd->data[i]) {
gid = atoi(sd->data[i++]);
} else {
i++;
}
}
dir = cmap.defaulthomedir;
if (sd->data[i]) {
if (strcmp(sd->data[i], "") == 0 ||
strcmp(sd->data[i], "NULL") == 0)
/* Leave dir pointing to the SQLDefaultHomedir, if any. */
i++;
else
dir = sd->data[i++];
}
if (cmap.shellfield)
shell = sd->data[i++];
else
shell = "";
if (uid < cmap.minuseruid)
uid = cmap.defaultuid;
if (gid < cmap.minusergid)
gid = cmap.defaultgid;
_sql_addpasswd(cmd, username, password, uid, gid, shell, dir);
}
} else {
/* retrieve our list of passwds */
where = _sql_where(cmd->tmp_pool, 1, cmap.userwhere );
mr = _sql_dispatch( _sql_make_cmd( cmd->tmp_pool, 4, "default",
cmap.usrtable, cmap.usrfield, where ),
"sql_select" );
_sql_check_response(mr);
sd = (sql_data_t *) mr->data;
for ( cnt = 0; cnt < sd->rnum; cnt++ ) {
username = sd->data[cnt];
/* if the username is NULL for whatever reason, skip it */
if ( username == NULL ) continue;
/* otherwise, add it to the cache */
lpw.pw_uid = -1;
lpw.pw_name = username;
_sql_getpasswd(cmd, &lpw);
}
}
cmap.passwd_cache_filled = 1;
cmap.curr_passwd = passwd_name_cache->head;
sql_log(DEBUG_FUNC, "%s", "<<< cmd_setpwent");
return DECLINED(cmd);
}
MODRET cmd_getpwent(cmd_rec *cmd) {
struct passwd *pw;
modret_t *mr;
if (!SQL_USERSET ||
!(cmap.engine & SQL_ENGINE_FL_AUTH))
return DECLINED(cmd);
sql_log(DEBUG_FUNC, "%s", ">>> cmd_getpwent");
/* make sure our passwd cache is complete */
if (!cmap.passwd_cache_filled) {
mr = cmd_setpwent(cmd);
if (mr->data == NULL) {
/* something didn't work in the setpwent call */
sql_log(DEBUG_FUNC, "%s", "<<< cmd_getpwent");
return DECLINED(cmd);
}
}
if (cmap.curr_passwd != NULL) {
pw = (struct passwd *) cmap.curr_passwd->data;
cmap.curr_passwd = cmap.curr_passwd->list_next;
} else {
pw = NULL;
}
sql_log(DEBUG_FUNC, "%s", "<<< cmd_getpwent");
if (pw == NULL ||
pw->pw_uid == (uid_t) -1)
return DECLINED(cmd);
return mod_create_data(cmd, (void *) pw);
}
MODRET cmd_endpwent(cmd_rec *cmd) {
if (!SQL_USERSET ||
!(cmap.engine & SQL_ENGINE_FL_AUTH))
return DECLINED(cmd);
sql_log(DEBUG_FUNC, "%s", ">>> cmd_endpwent");
cmap.curr_passwd = NULL;
sql_log(DEBUG_FUNC, "%s", "<<< cmd_endpwent");
return DECLINED(cmd);
}
MODRET cmd_setgrent(cmd_rec *cmd) {
modret_t *mr = NULL;
sql_data_t *sd = NULL;
int cnt = 0;
struct group lgr;
gid_t gid;
char *groupname = NULL;
char *grp_mem = NULL;
char *where = NULL;
array_header *ah =NULL;
char *iterator = NULL;
char *member = NULL;
if (!SQL_GROUPSET ||
!(cmap.engine & SQL_ENGINE_FL_AUTH))
return DECLINED(cmd);
sql_log(DEBUG_FUNC, "%s", ">>> cmd_setgrent");
/* if we've already filled the passwd group, just reset curr_group */
if (cmap.group_cache_filled) {
cmap.curr_group = group_name_cache->head;
sql_log(DEBUG_FUNC, "%s", "<<< cmd_setgrent");
return DECLINED(cmd);
}
if (SQL_FASTGROUPS) {
/* retrieve our list of groups */
where = _sql_where(cmd->tmp_pool, 1, cmap.groupwhere);
mr = _sql_dispatch(_sql_make_cmd(cmd->tmp_pool, 6, "default",
cmap.grptable, cmap.grpfields, where, NULL), "sql_select");
_sql_check_response(mr);
sd = (sql_data_t *) mr->data;
/* for each group, fill our array header and call _sql_addgroup */
for (cnt = 0; cnt < sd->rnum; cnt ++) {
/* if the groupname is NULL for whatever reason, skip the row */
groupname = sd->data[cnt * 3];
if (groupname == NULL)
continue;
gid = (gid_t) atol(sd->data[(cnt * 3) + 1]);
grp_mem = sd->data[(cnt * 3) + 2];
ah = make_array(cmd->tmp_pool, 10, sizeof(char *));
iterator = grp_mem;
for (member = strsep(&iterator, " ,"); member; member = strsep(&iterator, " ,")) {
if (*member == '\0')
continue;
*((char **) push_array(ah)) = member;
}
_sql_addgroup(cmd, groupname, gid, ah);
}
} else {
/* retrieve our list of groups */
where = _sql_where(cmd->tmp_pool, 1, cmap.groupwhere);
mr = _sql_dispatch(_sql_make_cmd(cmd->tmp_pool, 6, "default",
cmap.grptable, cmap.grpfield, where, NULL, "DISTINCT"), "sql_select");
_sql_check_response(mr);
sd = (sql_data_t *) mr->data;
for (cnt = 0; cnt < sd->rnum; cnt++) {
groupname = sd->data[cnt];
/* if the groupname is NULL for whatever reason, skip it */
if (groupname == NULL)
continue;
/* otherwise, add it to the cache */
lgr.gr_gid = -1;
lgr.gr_name = groupname;
_sql_getgroup(cmd, &lgr);
}
}
cmap.group_cache_filled = 1;
cmap.curr_group = group_name_cache->head;
sql_log(DEBUG_FUNC, "%s", "<<< cmd_setgrent");
return DECLINED(cmd);
}
MODRET cmd_getgrent(cmd_rec *cmd) {
struct group *gr;
modret_t *mr;
if (!SQL_GROUPSET ||
!(cmap.engine & SQL_ENGINE_FL_AUTH))
return DECLINED(cmd);
sql_log(DEBUG_FUNC, "%s", ">>> cmd_getgrent");
/* make sure our group cache is complete */
if (!cmap.group_cache_filled) {
mr = cmd_setgrent(cmd);
if (mr->data == NULL) {
/* something didn't work in the setgrent call */
sql_log(DEBUG_FUNC, "%s", "<<< cmd_getgrent");
return DECLINED(cmd);
}
}
if (cmap.curr_group != NULL) {
gr = (struct group *) cmap.curr_group->data;
cmap.curr_group = cmap.curr_group->list_next;
} else {
gr = NULL;
}
sql_log(DEBUG_FUNC, "%s", "<<< cmd_getgrent");
if (gr == NULL ||
gr->gr_gid == (gid_t) -1)
return DECLINED(cmd);
return mod_create_data(cmd, (void *) gr);
}
MODRET cmd_endgrent(cmd_rec *cmd) {
if (!SQL_GROUPSET ||
!(cmap.engine & SQL_ENGINE_FL_AUTH))
return DECLINED(cmd);
sql_log(DEBUG_FUNC, "%s", ">>> cmd_endgrent");
cmap.curr_group = NULL;
sql_log(DEBUG_FUNC, "%s", "<<< cmd_endgrent");
return DECLINED(cmd);
}
MODRET cmd_getpwnam(cmd_rec *cmd) {
struct passwd *pw;
struct passwd lpw;
if (!SQL_USERS ||
!(cmap.engine & SQL_ENGINE_FL_AUTH))
return DECLINED(cmd);
sql_log(DEBUG_FUNC, "%s", ">>> cmd_getpwnam");
lpw.pw_uid = -1;
lpw.pw_name = cmd->argv[0];
pw = _sql_getpasswd(cmd, &lpw);
if (pw == NULL ||
pw->pw_uid == (uid_t) -1) {
sql_log(DEBUG_FUNC, "%s", "<<< cmd_getpwnam");
return DECLINED(cmd);
}
sql_log(DEBUG_FUNC, "%s", "<<< cmd_getpwnam");
return mod_create_data(cmd, pw);
}
MODRET cmd_getpwuid(cmd_rec *cmd) {
struct passwd *pw;
struct passwd lpw;
if (!SQL_USERS ||
!(cmap.engine & SQL_ENGINE_FL_AUTH))
return DECLINED(cmd);
sql_log(DEBUG_FUNC, "%s", ">>> cmd_getpwuid");
lpw.pw_uid = (uid_t) cmd->argv[0];
lpw.pw_name = NULL;
pw = _sql_getpasswd(cmd, &lpw);
if (pw == NULL ||
pw->pw_uid == (uid_t) -1) {
sql_log(DEBUG_FUNC, "%s", "<<< cmd_getpwuid");
return DECLINED(cmd);
}
sql_log(DEBUG_FUNC, "%s", "<<< cmd_getpwuid");
return mod_create_data(cmd, pw);
}
MODRET cmd_getgrnam(cmd_rec *cmd) {
struct group *gr;
struct group lgr;
if (!SQL_GROUPS ||
!(cmap.engine & SQL_ENGINE_FL_AUTH))
return DECLINED(cmd);
sql_log(DEBUG_FUNC, "%s", ">>> cmd_getgrnam");
lgr.gr_gid = -1;
lgr.gr_name = cmd->argv[0];
gr = _sql_getgroup(cmd, &lgr);
if (gr == NULL ||
gr->gr_gid == (gid_t) -1) {
sql_log(DEBUG_FUNC, "%s", "<<< cmd_getgrnam");
return DECLINED(cmd);
}
sql_log(DEBUG_FUNC, "%s", "<<< cmd_getgrnam");
return mod_create_data(cmd, gr);
}
MODRET cmd_getgrgid(cmd_rec *cmd) {
struct group *gr;
struct group lgr;
if (!SQL_GROUPS ||
!(cmap.engine & SQL_ENGINE_FL_AUTH))
return DECLINED(cmd);
sql_log(DEBUG_FUNC, "%s", ">>> cmd_getgrgid");
lgr.gr_gid = (gid_t) cmd->argv[0];
lgr.gr_name = NULL;
gr = _sql_getgroup(cmd, &lgr);
if (gr == NULL ||
gr->gr_gid == (gid_t) -1) {
sql_log(DEBUG_FUNC, "%s", "<<< cmd_getgrgid");
return DECLINED(cmd);
}
sql_log(DEBUG_FUNC, "%s", "<<< cmd_getgrgid");
return mod_create_data(cmd, gr);
}
MODRET cmd_auth(cmd_rec *cmd) {
char *user = NULL;
struct passwd lpw, *pw;
modret_t *mr = NULL;
if (!SQL_USERS ||
!(cmap.engine & SQL_ENGINE_FL_AUTH))
return DECLINED(cmd);
sql_log(DEBUG_FUNC, "%s", ">>> cmd_auth");
user = cmd->argv[0];
/* escape our username */
mr = _sql_dispatch(_sql_make_cmd(cmd->tmp_pool, 2, "default", user),
"sql_escapestring" );
_sql_check_response(mr);
user = (char *) mr->data;
lpw.pw_uid = -1;
lpw.pw_name = cmd->argv[0];
if ((pw = _sql_getpasswd(cmd, &lpw)) &&
!auth_check(cmd->tmp_pool, pw->pw_passwd, cmd->argv[0], cmd->argv[1])) {
sql_log(DEBUG_FUNC, "%s", "<<< cmd_auth");
session.auth_mech = "mod_sql.c";
return HANDLED(cmd);
} else {
sql_log(DEBUG_FUNC, "%s", "<<< cmd_auth");
return DECLINED(cmd);
}
}
MODRET cmd_check(cmd_rec *cmd) {
/*
* should we bother to see if the hashed password is what we have in the
* database? or do we simply assume it is, and ignore the fact that we're
* being passed the username, too?
*/
array_header *ah = cmap.authlist;
auth_type_entry *auth_entry;
char *c_hash;
char *c_clear;
int success = 0;
int cnt = 0;
struct passwd lpw;
struct stat st;
modret_t *mr = NULL;
if (!SQL_USERS ||
!(cmap.engine & SQL_ENGINE_FL_AUTH))
return DECLINED(cmd);
sql_log(DEBUG_FUNC, "%s", ">>> cmd_check");
if (cmd->argv[0] == NULL) {
sql_log(DEBUG_AUTH, "%s", "NULL hashed password");
} else if (cmd->argv[1] == NULL) {
sql_log(DEBUG_AUTH, "%s", "NULL user name");
} else if (cmd->argv[2] == NULL) {
sql_log(DEBUG_AUTH, "%s", "NULL clear password");
} else {
c_hash = cmd->argv[0];
c_clear = cmd->argv[2];
if (!ah)
sql_log(DEBUG_AUTH, "%s", "warning: no SQLAuthTypes configured");
for (cnt = 0; ah && cnt < ah->nelts; cnt++) {
auth_entry = ((auth_type_entry **) ah->elts)[cnt];
sql_log(DEBUG_AUTH, "checking auth_type %s", auth_entry->name);
mr = auth_entry->check_function(cmd, c_clear, c_hash);
if (!MODRET_ISERROR(mr)) {
sql_log(DEBUG_AUTH, "'%s' auth handler reports success",
auth_entry->name);
success = 1;
break;
}
}
}
if (success) {
/* this and the associated hack in cmd_uid2name are to support
* uid reuse in the database -- people (for whatever reason) are
* reusing uids/gids multiple times, and the displayed owner in a
* LIST or NLST needs to match the current user if possible. This
* depends on the fact that if we get success, the user exists in the
* database ( -- is this always true? ).
*/
lpw.pw_uid = -1;
lpw.pw_name = cmd->argv[1];
cmap.authpasswd = _sql_getpasswd(cmd, &lpw);
/*
* finally, build the user's homedir if necessary
*/
if (cmap.buildhomedir && cmap.authpasswd &&
(stat(cmap.authpasswd->pw_dir, &st) == -1 && errno == ENOENT)) {
build_homedir(cmd, cmap.authpasswd->pw_dir,
S_IRWXU | S_IRWXG | S_IRWXO,
cmap.authpasswd->pw_uid,
cmap.authpasswd->pw_gid);
}
session.auth_mech = "mod_sql.c";
sql_log(DEBUG_FUNC, "%s", "<<< cmd_check");
return HANDLED(cmd);
}
sql_log(DEBUG_FUNC, "%s", "<<< cmd_check");
if (!success)
return DECLINED(cmd);
return mr;
}
MODRET cmd_uid2name(cmd_rec *cmd) {
char *uid_name = NULL;
struct passwd *pw;
struct passwd lpw;
char uidstr[MOD_SQL_BUFSIZE] = {'\0'};
if (!SQL_USERS ||
!(cmap.engine & SQL_ENGINE_FL_AUTH))
return DECLINED(cmd);
sql_log(DEBUG_FUNC, "%s", ">>> cmd_uid2name");
lpw.pw_uid = *((uid_t *) cmd->argv[0]);
lpw.pw_name = NULL;
/* check to see if we're looking up the current user */
if (cmap.authpasswd &&
lpw.pw_uid == cmap.authpasswd->pw_uid) {
sql_log(DEBUG_INFO, "%s", "matched current user");
pw = cmap.authpasswd;
} else {
pw = _sql_getpasswd(cmd, &lpw);
}
sql_log(DEBUG_FUNC, "%s", "<<< cmd_uid2name");
if (pw == NULL)
return DECLINED(cmd);
/* In the case of a lookup of a negatively cached UID, the pw_name
* member will be NULL, which causes an undesired handling by
* the core code. Handle this case separately.
*/
if (pw->pw_name)
uid_name = pw->pw_name;
else {
snprintf(uidstr, MOD_SQL_BUFSIZE, "%lu", (unsigned long) cmd->argv[0]);
uid_name = uidstr;
}
return mod_create_data(cmd, uid_name);
}
MODRET cmd_gid2name(cmd_rec *cmd) {
char *gid_name = NULL;
struct group *gr;
struct group lgr;
char gidstr[MOD_SQL_BUFSIZE]={'\0'};
if (!SQL_GROUPS ||
!(cmap.engine & SQL_ENGINE_FL_AUTH))
return DECLINED(cmd);
sql_log(DEBUG_FUNC, "%s", ">>> cmd_gid2name");
lgr.gr_gid = *((gid_t *) cmd->argv[0]);
lgr.gr_name = NULL;
gr = _sql_getgroup(cmd, &lgr);
sql_log(DEBUG_FUNC, "%s", "<<< cmd_gid2name");
if (gr == NULL)
return DECLINED(cmd);
/* In the case of a lookup of a negatively cached GID, the gr_name
* member will be NULL, which causes an undesired handling by
* the core code. Handle this case separately.
*/
if (gr->gr_name)
gid_name = gr->gr_name;
else {
snprintf(gidstr, MOD_SQL_BUFSIZE, "%lu", (unsigned long) cmd->argv[0]);
gid_name = gidstr;
}
return mod_create_data(cmd, gid_name);
}
MODRET cmd_name2uid(cmd_rec *cmd) {
struct passwd *pw;
struct passwd lpw;
if (!SQL_USERS ||
!(cmap.engine & SQL_ENGINE_FL_AUTH))
return DECLINED(cmd);
sql_log(DEBUG_FUNC, "%s", ">>> cmd_name2uid");
lpw.pw_uid = -1;
lpw.pw_name = cmd->argv[0];
/* check to see if we're looking up the current user */
if (cmap.authpasswd &&
strcmp(lpw.pw_name, cmap.authpasswd->pw_name) == 0) {
sql_log(DEBUG_INFO, "%s", "matched current user");
pw = cmap.authpasswd;
} else {
pw = _sql_getpasswd(cmd, &lpw);
}
if (pw == NULL ||
pw->pw_uid == (uid_t) -1) {
sql_log(DEBUG_FUNC, "%s", "<<< cmd_name2uid");
return DECLINED(cmd);
}
sql_log(DEBUG_FUNC, "%s", "<<< cmd_name2uid");
return mod_create_data(cmd, (void *) &pw->pw_uid);
}
MODRET cmd_name2gid(cmd_rec *cmd) {
struct group *gr;
struct group lgr;
if (!SQL_GROUPS ||
!(cmap.engine & SQL_ENGINE_FL_AUTH))
return DECLINED(cmd);
sql_log(DEBUG_FUNC, "%s", ">>> cmd_name2gid");
lgr.gr_gid = -1;
lgr.gr_name = cmd->argv[0];
gr = _sql_getgroup(cmd, &lgr);
if (gr == NULL ||
gr->gr_gid == (gid_t) -1) {
sql_log(DEBUG_FUNC, "%s", "<<< cmd_name2gid");
return DECLINED(cmd);
}
sql_log(DEBUG_FUNC, "%s", "<<< cmd_name2gid");
return mod_create_data(cmd, (void *) &gr->gr_gid);
}
MODRET cmd_getgroups(cmd_rec *cmd) {
int res;
if (!SQL_GROUPS ||
!(cmap.engine & SQL_ENGINE_FL_AUTH))
return DECLINED(cmd);
sql_log(DEBUG_FUNC, "%s", ">>> cmd_getgroups");
res = _sql_getgroups(cmd);
if (res < 0) {
sql_log(DEBUG_FUNC, "%s", "<<< cmd_getgroups");
return DECLINED(cmd);
}
sql_log(DEBUG_FUNC, "%s", "<<< cmd_getgroups");
return mod_create_data(cmd, (void *) &res);
}
MODRET cmd_getstats(cmd_rec *cmd) {
modret_t *mr;
char *query;
sql_data_t *sd;
char *usrwhere, *where;
sql_log(DEBUG_FUNC, "%s", ">>> cmd_getstats");
if (!cmap.sql_fstor) {
return DECLINED(cmd);
}
usrwhere = pstrcat(cmd->tmp_pool, cmap.usrfield, " = '", _sql_realuser(cmd), "'", NULL);
where = _sql_where(cmd->tmp_pool, 2, usrwhere, cmap.userwhere );
query = pstrcat(cmd->tmp_pool, cmap.sql_fstor, ", ",
cmap.sql_fretr, ", ", cmap.sql_bstor, ", ",
cmap.sql_bretr, NULL );
mr = _sql_dispatch( _sql_make_cmd( cmd->tmp_pool, 4, "default",
cmap.usrtable, query, where ),
"sql_select" );
_sql_check_response(mr);
sql_log(DEBUG_FUNC, "%s", "<<< cmd_getstats");
sd = mr->data;
if (sd->rnum == 0)
return ERROR(cmd);
return mod_create_data(cmd, sd->data);
}
MODRET cmd_getratio(cmd_rec *cmd) {
modret_t *mr;
char *query;
sql_data_t *sd;
char *usrwhere, *where;
if (!cmap.sql_frate)
return DECLINED(cmd);
sql_log(DEBUG_FUNC, "%s", ">>> cmd_getratio");
usrwhere = pstrcat(cmd->tmp_pool, cmap.usrfield, " = '", _sql_realuser(cmd), "'", NULL);
where = _sql_where(cmd->tmp_pool, 2, usrwhere, cmap.userwhere );
query = pstrcat(cmd->tmp_pool, cmap.sql_frate, ", ",
cmap.sql_fcred, ", ", cmap.sql_brate, ", ",
cmap.sql_bcred, NULL);
mr = _sql_dispatch( _sql_make_cmd( cmd->tmp_pool, 4, "default",
cmap.usrtable, query, where ),
"sql_select" );
_sql_check_response(mr);
sql_log(DEBUG_FUNC, "%s", "<<< cmd_getratio");
sd = mr->data;
if (sd->rnum == 0)
return ERROR(cmd);
return mod_create_data(cmd, sd->data);
}
/*****************************************************************
*
* CONFIGURATION DIRECTIVE HANDLERS
*
*****************************************************************/
MODRET set_sqlratiostats(cmd_rec * cmd)
{
int b;
CHECK_CONF(cmd, CONF_ROOT | CONF_GLOBAL);
switch (cmd->argc - 1) {
default:
CONF_ERROR(cmd, "requires a boolean or 4 field names: "
"fstor fretr bstor bretr");
case 1:
if ((b = get_boolean(cmd, 1)) == -1)
CONF_ERROR(cmd, "requires a boolean or 4 field names: "
"fstor fretr bstor bretr");
if (b)
add_config_param_str("SQLRatioStats", 4,
"fstor", "fretr", "bstor", "bretr");
break;
case 4:
add_config_param_str("SQLRatioStats", 4,
(void *) cmd->argv[1], (void *) cmd->argv[2],
(void *) cmd->argv[3], (void *) cmd->argv[4]);
}
return HANDLED(cmd);
}
MODRET set_sqlnegativecache(cmd_rec *cmd) {
int bool = -1;
config_rec *c = NULL;
CHECK_ARGS(cmd, 1);
CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
bool = get_boolean(cmd, 1);
if (bool == -1)
CONF_ERROR(cmd, "expected a Boolean parameter");
c = add_config_param(cmd->argv[0], 1, NULL);
c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
*((unsigned char *) c->argv[0]) = bool;
return HANDLED(cmd);
}
MODRET set_sqlratios(cmd_rec * cmd)
{
int b;
CHECK_CONF(cmd, CONF_ROOT | CONF_GLOBAL);
switch (cmd->argc - 1) {
default:
CONF_ERROR(cmd, "requires a boolean or 4 field names: "
"frate fcred brate bcred");
case 1:
if ((b = get_boolean(cmd, 1)) == -1)
CONF_ERROR(cmd, "requires a boolean or 4 field names: "
"frate fcred brate bcred");
if (b)
add_config_param_str("SQLRatios", 4,
"frate", "fcred", "brate", "bcred");
break;
case 4:
add_config_param_str("SQLRatios", 4,
(void *) cmd->argv[1], (void *) cmd->argv[2],
(void *) cmd->argv[3], (void *) cmd->argv[4]);
}
return HANDLED(cmd);
}
MODRET add_virtualstr(char *name, cmd_rec *cmd) {
CHECK_ARGS(cmd, 1);
CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL);
add_config_param_str(name, 1, (void *) cmd->argv[1]);
return HANDLED(cmd);
}
MODRET add_virtualbool(char *name, cmd_rec *cmd) {
int bool;
config_rec *c;
CHECK_ARGS(cmd, 1);
CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL);
bool = get_boolean(cmd, 1);
if (bool == -1)
CONF_ERROR(cmd, "requires a Boolean parameter");
c = add_config_param(name, 1, NULL);
c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
*((unsigned char *) c->argv[0]) = bool;
c->flags |= CF_MERGEDOWN;
return HANDLED(cmd);
}
/* usage: SQLUserInfo table(s) usernamefield passwdfield uid gid homedir
* shell | custom:/<sql-named-query>
*/
MODRET set_sqluserinfo(cmd_rec *cmd) {
if (cmd->argc-1 != 1 &&
cmd->argc-1 != 7)
CONF_ERROR(cmd, "missing parameters");
CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL);
if (cmd->argc-1 == 1) {
char *tmp = NULL;
/* If only one paramter is used, it must be of the "custom:/" form. */
if (strncmp("custom:/", cmd->argv[1], 8) != 0)
CONF_ERROR(cmd, "badly formatted parameter");
tmp = strchr(cmd->argv[1], '/');
add_config_param_str("SQLCustomUserInfo", 1, tmp+1);
return HANDLED(cmd);
}
/* required to exist - not even going to check them. */
add_config_param_str("SQLUserTable", 1, (void *) cmd->argv[1]);
add_config_param_str("SQLUsernameField", 1, (void *) cmd->argv[2]);
add_config_param_str("SQLPasswordField", 1, (void *) cmd->argv[3]);
/* These could be "NULL" */
if (strncasecmp("null", cmd->argv[4], 4) != 0)
add_config_param_str("SQLUidField", 1, (void *) cmd->argv[4]);
if (strncasecmp("null", cmd->argv[5], 4) != 0)
add_config_param_str("SQLGidField", 1, (void *) cmd->argv[5]);
if (strncasecmp("null", cmd->argv[6], 4) != 0)
add_config_param_str("SQLHomedirField", 1, (void *) cmd->argv[6]);
if (strncasecmp("null", cmd->argv[7], 4) != 0)
add_config_param_str("SQLShellField", 1, (void *) cmd->argv[7]);
return HANDLED(cmd);
}
MODRET set_sqluserwhereclause(cmd_rec *cmd) {
return add_virtualstr("SQLUserWhereClause", cmd);
}
/* usage: SQLGroupInfo table(s) groupnamefield gidfield membersfield */
MODRET set_sqlgroupinfo(cmd_rec *cmd) {
CHECK_ARGS(cmd, 4);
CHECK_CONF(cmd, CONF_ROOT | CONF_GLOBAL | CONF_VIRTUAL);
/* required to exist - not even going to check them. */
add_config_param_str("SQLGroupTable", 1, (void *) cmd->argv[1]);
add_config_param_str("SQLGroupnameField", 1, (void *) cmd->argv[2]);
add_config_param_str("SQLGroupGIDField", 1, (void *) cmd->argv[3]);
add_config_param_str("SQLGroupMembersField", 1, (void *) cmd->argv[4]);
return HANDLED(cmd);
}
MODRET set_sqlgroupwhereclause(cmd_rec *cmd) {
return add_virtualstr("SQLGroupWhereClause", cmd);
}
MODRET set_sqldefaulthomedir(cmd_rec *cmd) {
return add_virtualstr("SQLDefaultHomedir", cmd);
}
MODRET set_sqlhomedirondemand(cmd_rec *cmd) {
return add_virtualbool("SQLHomedirOnDemand", cmd);
}
/* usage: SQLLog cmdlist query-name */
MODRET set_sqllog(cmd_rec *cmd) {
config_rec * c;
char *name, *namep;
char *cmds;
char *iterator;
CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL);
if (cmd->argc < 3 ||
cmd->argc > 4) {
CONF_ERROR(cmd, "expected cmdlist query-name [IGNORE_ERRORS]");
}
/* For each element in the command list, add a 'SQLLog_CMD' config_rec..
* this is an optimization that speeds up logging and also simplifies the
* logging code, since there's no need to run through and parse a bunch
* of potenitally unused SQLLog statements each time any command is run.
*/
cmds = cmd->argv[1];
iterator = cmds;
for (name = strsep(&iterator, ", "); name; name = strsep(&iterator, ", ")) {
if (*name == '\0')
continue;
for (namep = name; *namep != '\0'; namep++)
*namep = toupper(*namep);
name = pstrcat(cmd->tmp_pool, "SQLLog_", name, NULL);
if (cmd->argc == 4 &&
strcasecmp(cmd->argv[3], "IGNORE_ERRORS") == 0) {
c = add_config_param_str(name, 2, cmd->argv[2], "ignore");
} else {
c = add_config_param_str(name, 1, cmd->argv[2]);
}
c->flags |= CF_MERGEDOWN_MULTI;
}
return HANDLED(cmd);
}
MODRET set_sqllogfile(cmd_rec *cmd) {
CHECK_ARGS(cmd, 1);
CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
return HANDLED(cmd);
}
/* usage: SQLNamedQuery name type query-string */
MODRET set_sqlnamedquery(cmd_rec *cmd) {
config_rec *c = NULL;
char *name = NULL;
CHECK_CONF(cmd, CONF_ROOT | CONF_GLOBAL | CONF_VIRTUAL);
if (cmd->argc < 3) {
CONF_ERROR( cmd, "requires at least 2 arguments" );
}
name = pstrcat(cmd->tmp_pool, "SQLNamedQuery_", cmd->argv[1], NULL);
if (strcasecmp(cmd->argv[2], "SELECT") == 0) {
if (cmd->argc != 4)
CONF_ERROR(cmd, "expected 'SELECT' query-string");
c = add_config_param_str(name, 2, SQL_SELECT_C, cmd->argv[3]);
} else if (strcasecmp(cmd->argv[2], "FREEFORM") == 0) {
if (cmd->argc != 4)
CONF_ERROR(cmd, "expected 'FREEFORM' query-string");
c = add_config_param_str(name, 2, SQL_FREEFORM_C, cmd->argv[3]);
} else if (strcasecmp(cmd->argv[2], "INSERT") == 0) {
if (cmd->argc != 5)
CONF_ERROR(cmd, "expected 'INSERT' query-string table-name");
c = add_config_param_str(name, 3, SQL_INSERT_C, cmd->argv[3],
cmd->argv[4]);
} else if (strcasecmp(cmd->argv[2], "UPDATE") == 0) {
if (cmd->argc != 5)
CONF_ERROR(cmd, "expected 'UPDATE' query-string table-name");
c = add_config_param_str(name, 3, SQL_UPDATE_C, cmd->argv[3],
cmd->argv[4]);
} else {
CONF_ERROR(cmd, "type must be SELECT, INSERT, UPDATE, or FREEFORM");
}
c->flags |= CF_MERGEDOWN;
return HANDLED(cmd);
}
/* usage: SQLShowInfo cmdlist numeric format-string */
MODRET set_sqlshowinfo(cmd_rec *cmd) {
config_rec *c = NULL;
char *iterator = NULL;
char *namep = NULL;
char *name = NULL;
char *cmds = NULL;
CHECK_ARGS(cmd, 3);
CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL);
cmds = pstrdup(cmd->tmp_pool, cmd->argv[1]);
iterator = cmds;
for (name = strsep(&iterator, ", "); name; name = strsep(&iterator, ", ")) {
if (*name == '\0')
continue;
for (namep = name; *namep != '\0'; namep++)
*namep = toupper(*namep);
name = pstrcat(cmd->tmp_pool, "SQLShowInfo_", name, NULL);
c = add_config_param_str(name, 2, cmd->argv[2], cmd->argv[3]);
c->flags |= CF_MERGEDOWN_MULTI;
}
return HANDLED(cmd);
}
MODRET set_sqlauthenticate(cmd_rec *cmd) {
config_rec *c = NULL;
char *arg = NULL;
int authmask = 0;
int cnt = 0;
int groupset_flag = 0;
int userset_flag = 0;
int groups_flag = 0;
int users_flag = 0;
CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL);
if (cmd->argc < 2 ||
cmd->argc > 5)
CONF_ERROR(cmd, "requires 1 to 4 arguments. Check the mod_sql docs");
/* We're setting our authmask here -- we have a bunch of checks needed to
* make sure users aren't trying to screw around with us.
*/
if (cmd->argc == 2 &&
strcasecmp(cmd->argv[1], "on") == 0) {
authmask = SQL_AUTH_GROUPSET|SQL_AUTH_USERSET|SQL_AUTH_USERS|
SQL_AUTH_GROUPS;
} else if (!((cmd->argc == 2) && !strcasecmp(cmd->argv[1], "off"))) {
for (cnt = 1; cnt < cmd->argc; cnt++) {
arg = cmd->argv[cnt];
if (strncasecmp("groupset", arg, 8) == 0) {
if (groupset_flag)
CONF_ERROR(cmd, "groupset already set");
if (strcasecmp("groupsetfast", arg) == 0) {
authmask |= SQL_FAST_GROUPSET;
} else if (strlen(arg) > 8) {
CONF_ERROR(cmd, "unknown argument");
}
authmask |= SQL_AUTH_GROUPSET;
groupset_flag = 1;
} else if (strncasecmp("userset", arg, 7) == 0) {
if (userset_flag)
CONF_ERROR(cmd, "userset already set");
if (strcasecmp("usersetfast", arg) == 0) {
authmask |= SQL_FAST_USERSET;
} else if (strlen(arg) > 7) {
CONF_ERROR(cmd, "unknown argument");
}
authmask |= SQL_AUTH_USERSET;
userset_flag = 1;
} else if (strncasecmp("groups", arg, 6) == 0) {
if (groups_flag)
CONF_ERROR(cmd, "groups already set");
if (strcasecmp("groups*", arg) == 0) {
pr_log_debug(DEBUG1, "%s: use of * in SQLAuthenticate has been deprecated. Use AuthOrder for setting authoritativeness", cmd->argv[0]);
} else if (strlen(arg) > 6) {
CONF_ERROR(cmd, "unknown argument");
}
authmask |= SQL_AUTH_GROUPS;
groups_flag = 1;
} else if (strncasecmp("users", arg, 5) == 0) {
if (users_flag)
CONF_ERROR(cmd, "users already set");
if (strcasecmp("users*", arg) == 0) {
pr_log_debug(DEBUG1, "%s: use of * in SQLAuthenticate has been deprecated. Use AuthOrder for setting authoritativeness", cmd->argv[0]);
} else if (strlen(arg) > 5) {
CONF_ERROR(cmd, "unknown argument");
}
authmask |= SQL_AUTH_USERS;
users_flag = 1;
} else {
CONF_ERROR(cmd, "unknown argument");
}
}
}
/* Finally, fixup if we've received groupset with no groups,
* or userset with no users
*/
if ((groupset_flag && !groups_flag) ||
(userset_flag && !users_flag)) {
CONF_ERROR(cmd, "groupset and userset have no meaning without "
"a corresponding groups or users argument.");
}
c = add_config_param(cmd->argv[0], 1, NULL);
c->argv[0] = pcalloc(c->pool, sizeof(int));
*((int *) c->argv[0]) = authmask;
c->flags |= CF_MERGEDOWN;
return HANDLED(cmd);
}
/* logging stuff */
static char *sql_logfile = NULL;
static int sql_logfd = -1;
static int sql_closelog(void) {
/* sanity check */
if (sql_logfd != -1) {
close(sql_logfd);
sql_logfd = -1;
sql_logfile = NULL;
}
return 0;
}
int sql_log(int level, const char *fmt, ...) {
char buf[PR_TUNABLE_BUFFER_SIZE] = {'\0'};
time_t timestamp = time(NULL);
struct tm *t = NULL;
va_list msg;
/* sanity check */
if (!sql_logfile)
return 0;
t = localtime(×tamp);
/* prepend the timestamp */
strftime(buf, sizeof(buf), "%b %d %H:%M:%S ", t);
buf[sizeof(buf) - 1] = '\0';
/* prepend a small header */
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
MOD_SQL_VERSION "[%u]: ", (unsigned int) getpid());
buf[sizeof(buf) - 1] = '\0';
/* affix the message */
va_start(msg, fmt);
vsnprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), fmt, msg);
va_end(msg);
buf[sizeof(buf) - 1] = '\0';
buf[strlen(buf)] = '\n';
if (write(sql_logfd, buf, strlen(buf)) < 0)
return -1;
return 0;
}
static int sql_openlog(void) {
int res = 0;
/* Sanity checks */
if ((sql_logfile = get_param_ptr(main_server->conf, "SQLLogFile",
FALSE)) == NULL)
return 0;
if (!strcasecmp(sql_logfile, "none")) {
sql_logfile = NULL;
return 0;
}
pr_signals_block();
PRIVS_ROOT
res = pr_log_openfile(sql_logfile, &sql_logfd, 0640);
PRIVS_RELINQUISH
pr_signals_unblock();
return res;
}
MODRET set_sqlconnectinfo(cmd_rec *cmd) {
config_rec *c;
char *info = NULL;
char *user = "";
char *pass = "";
char *ttl = NULL;
CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL);
if (cmd->argc < 2 ||
cmd->argc > 5)
CONF_ERROR(cmd, "requires 1 to 4 arguments. Check the mod_sql docs");
if (cmd->argc > 1)
info = cmd->argv[1];
if (cmd->argc > 2)
user = cmd->argv[2];
if (cmd->argc > 3)
pass = cmd->argv[3];
if (cmd->argc > 4)
ttl = cmd->argv[4];
else
ttl = "0";
c = add_config_param_str(cmd->argv[0], 4, info, user, pass, ttl);
c->flags |= CF_MERGEDOWN;
return HANDLED(cmd);
}
/* usage: SQLEngine on|off|auth|log */
MODRET set_sqlengine(cmd_rec *cmd) {
config_rec *c;
int engine = -1;
CHECK_ARGS(cmd, 1);
CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL|CONF_ANON);
engine = get_boolean(cmd, 1);
if (engine == -1) {
/* The parameter is not a boolean; check for "auth" or "log". */
if (strcasecmp(cmd->argv[1], "auth") == 0)
engine = SQL_ENGINE_FL_AUTH;
else if (strcasecmp(cmd->argv[1], "log") == 0)
engine = SQL_ENGINE_FL_LOG;
else
CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unknown SQLEngine parameter '",
cmd->argv[1], "'", NULL));
} else {
if (engine == 1)
/* Convert an "on" into a auth|log combination. */
engine = SQL_ENGINE_FL_AUTH|SQL_ENGINE_FL_LOG;
}
c = add_config_param(cmd->argv[0], 1, NULL);
c->argv[0] = pcalloc(c->pool, sizeof(int));
*((int *) c->argv[0]) = engine;
c->flags = CF_MERGEDOWN;
return HANDLED(cmd);
}
MODRET set_sqlauthtypes(cmd_rec *cmd) {
config_rec *c;
array_header *ah;
auth_type_entry *auth_entry;
auth_type_entry **auth_handle;
int cnt;
CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL);
/* Need *at least* one handler. */
if (cmd->argc < 2)
CONF_ERROR(cmd, "expected at least one handler type");
ah = make_array(permanent_pool, cmd->argc - 1, sizeof(auth_type_entry *));
/* Walk through our cmd->argv. */
for (cnt = 1; cnt < cmd->argc; cnt++) {
auth_entry = get_auth_entry(cmd->argv[cnt]);
if (auth_entry == NULL) {
sql_log(DEBUG_WARN, "unknown auth handler '%s'", cmd->argv[cnt]);
CONF_ERROR(cmd, "unknown auth handler");
}
auth_handle = (auth_type_entry **) push_array(ah);
*auth_handle = auth_entry;
}
c = add_config_param(cmd->argv[0], 1, (void *) ah);
c->flags |= CF_MERGEDOWN;
return HANDLED(cmd);
}
/* usage: SQLBackend name */
MODRET set_sqlbackend(cmd_rec *cmd) {
CHECK_ARGS(cmd, 1);
CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL);
add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
return HANDLED(cmd);
}
MODRET set_sqlminid(cmd_rec *cmd) {
config_rec *c;
unsigned long val;
char *endptr = NULL;
CHECK_ARGS(cmd, 1);
CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL);
val = strtoul(cmd->argv[1], &endptr, 10);
if (*endptr != '\0')
CONF_ERROR(cmd, "requires a numeric argument");
/* Whee! need to check if in the legal range for uid_t and gid_t. */
if (val == ULONG_MAX &&
errno == ERANGE) {
CONF_ERROR(cmd, "the value given is outside the legal range");
}
c = add_config_param(cmd->argv[0], 1, NULL);
c->argv[0] = pcalloc(c->pool, sizeof(unsigned long));
*((unsigned long *) c->argv[0]) = val;
c->flags |= CF_MERGEDOWN;
return HANDLED(cmd);
}
MODRET set_sqlminuseruid(cmd_rec *cmd) {
config_rec *c = NULL;
unsigned long val;
char *endptr = NULL;
CHECK_ARGS(cmd, 1);
CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL);
val = strtoul(cmd->argv[1], &endptr, 10);
if (*endptr != '\0')
CONF_ERROR(cmd, "requires a numeric argument");
/* Whee! need to check if in the legal range for uid_t. */
if (val == ULONG_MAX &&
errno == ERANGE) {
CONF_ERROR(cmd, "the value given is outside the legal range");
}
c = add_config_param(cmd->argv[0], 1, NULL);
c->argv[0] = pcalloc(c->pool, sizeof(uid_t));
*((uid_t *) c->argv[0]) = val;
c->flags |= CF_MERGEDOWN;
return HANDLED(cmd);
}
MODRET set_sqlminusergid(cmd_rec *cmd) {
config_rec *c = NULL;
unsigned long val;
char *endptr = NULL;
CHECK_ARGS(cmd, 1);
CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL);
val = strtoul(cmd->argv[1], &endptr, 10);
if (*endptr != '\0')
CONF_ERROR(cmd, "requires a numeric argument");
/* Whee! need to check if in the legal range for gid_t. */
if (val == ULONG_MAX &&
errno == ERANGE) {
CONF_ERROR(cmd, "the value given is outside the legal range");
}
c = add_config_param(cmd->argv[0], 1, NULL);
c->argv[0] = pcalloc(c->pool, sizeof(gid_t));
*((gid_t *) c->argv[0]) = val;
c->flags |= CF_MERGEDOWN;
return HANDLED(cmd);
}
MODRET set_sqldefaultuid(cmd_rec *cmd) {
config_rec *c;
uid_t val;
char *endptr = NULL;
CHECK_ARGS(cmd, 1);
CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL);
val = strtoul(cmd->argv[1], &endptr, 10);
if (*endptr != '\0')
CONF_ERROR(cmd, "requires a numeric argument");
/* Whee! need to check is in the legal range for uid_t. */
if (val == ULONG_MAX &&
errno == ERANGE) {
CONF_ERROR(cmd, "the value given is outside the legal range");
}
c = add_config_param(cmd->argv[0], 1, NULL);
c->argv[0] = pcalloc(c->pool, sizeof(uid_t));
*((uid_t *) c->argv[0]) = val;
c->flags |= CF_MERGEDOWN;
return HANDLED(cmd);
}
MODRET set_sqldefaultgid(cmd_rec *cmd) {
config_rec *c;
gid_t val;
char *endptr = NULL;
CHECK_ARGS(cmd, 1);
CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL);
val = strtoul(cmd->argv[1], &endptr, 10);
if (*endptr != '\0')
CONF_ERROR(cmd, "requires a numeric argument");
/* Whee! need to check is in the legal range for gid_t. */
if (val == ULONG_MAX &&
errno == ERANGE) {
CONF_ERROR(cmd, "the value given is outside the legal range");
}
c = add_config_param(cmd->argv[0], 1, NULL);
c->argv[0] = pcalloc(c->pool, sizeof(gid_t));
*((gid_t *) c->argv[0]) = val;
c->flags |= CF_MERGEDOWN;
return HANDLED(cmd);
}
/* Event handlers
*/
static void sql_exit_ev(const void *event_data, void *user_data) {
config_rec *c;
cmd_rec *cmd;
modret_t *mr;
/* Note: most of this code hacked out of log_master(), which
* brings to mind the idea of reorganizing the code a little, so
* that this function can call a function to do this, instead of
* handling it itself; that function to be used by/in log_master
* as well.
*/
if (cmap.engine == 0)
return;
/* handle EXIT queries */
c = find_config(main_server->conf, CONF_PARAM, "SQLLog_EXIT", FALSE);
while (c) {
char *qname = NULL, *type = NULL;
qname = c->argv[0];
/* Since we're exiting the process here (or soon, anyway), we can
* get away with using the config_rec's pool.
*/
cmd = _sql_make_cmd(c->pool, 1, "EXIT");
type = _named_query_type(cmd, qname);
if (type) {
if (strcasecmp(type, SQL_UPDATE_C) == 0 ||
strcasecmp(type, SQL_FREEFORM_C) == 0 ||
strcasecmp(type, SQL_INSERT_C) == 0) {
sql_log(DEBUG_FUNC, "running named query '%s' at exit", qname);
_process_named_query( cmd, qname );
} else {
sql_log(DEBUG_WARN, "named query '%s' is not an INSERT, UPDATE, or "
"FREEFORM query", qname);
}
} else
sql_log(DEBUG_WARN, "named query '%s' cannot be found", qname);
c = find_config_next(c, c->next, CONF_PARAM, "SQLLog_EXIT", FALSE);
}
cmd = _sql_make_cmd(session.pool, 0);
mr = _sql_dispatch(cmd, "sql_exit");
_sql_check_response(mr);
sql_closelog();
return;
}
#if defined(PR_SHARED_MODULE)
static void sql_mod_unload_ev(const void *event_data, void *user_data) {
if (strcmp("mod_sql.c", (const char *) event_data) == 0) {
destroy_pool(sql_pool);
sql_pool = NULL;
sql_backends = NULL;
pr_event_unregister(&sql_module, NULL, NULL);
}
}
#else
static void sql_preparse_ev(const void *event_data, void *user_data) {
/* If no backends have been registered, croak. */
if (sql_nbackends == 0) {
pr_log_pri(PR_LOG_NOTICE, MOD_SQL_VERSION
": notice: no backend modules have been registered");
exit(1);
}
}
#endif /* PR_SHARED_MODULE */
/* Initialization routines
*/
static int sql_init(void) {
#if defined(PR_SHARED_MODULE)
pr_event_register(&sql_module, "core.module-unload", sql_mod_unload_ev, NULL);
#else
pr_event_register(&sql_module, "core.preparse", sql_preparse_ev, NULL);
#endif /* PR_SHARED_MODULE */
return 0;
}
static int sql_sess_init(void) {
char *authstr = NULL;
config_rec *c = NULL;
void *temp_ptr = NULL;
unsigned char *negative_cache = NULL;
cmd_rec *cmd = NULL;
modret_t *mr = NULL;
sql_data_t *sd = NULL;
int percall = 0, res = 0;
char *fieldset = NULL;
pool *tmp_pool = NULL;
/* Build a temporary pool */
tmp_pool = make_sub_pool(session.pool);
/* Open any configured SQLLogFile */
res = sql_openlog();
if (res < 0) {
if (res == -1)
pr_log_pri(PR_LOG_NOTICE, "notice: unable to open SQLLogFile: %s",
strerror(errno));
else if (res == PR_LOG_WRITABLE_DIR)
pr_log_pri(PR_LOG_NOTICE, "notice: unable to open SQLLogFile: "
"parent directory is world writeable");
else if (res == PR_LOG_SYMLINK)
pr_log_pri(PR_LOG_NOTICE, "notice: unable to open SQLLogFile: "
"cannot log to a symbolic link");
}
/* Determine which backend to use.
*
* If there is only one registered backend to use, the decision is easy.
*
* If there are more than one backends, default to using the first
* entry in the linked list (last backend module registered). Check
* for a configured SQLBackend directive, to see if a specific backend
* has been requested.
*/
if (sql_nbackends == 1) {
sql_log(DEBUG_INFO, "defaulting to '%s' backend", sql_backends->backend);
sql_cmdtable = sql_backends->cmdtab;
} else if (sql_nbackends > 1) {
temp_ptr = get_param_ptr(main_server->conf, "SQLBackend", FALSE);
if (temp_ptr) {
struct sql_backend *b;
for (b = sql_backends; b; b = b->next) {
if (strcasecmp(b->backend, (char *) temp_ptr) == 0) {
sql_log(DEBUG_INFO, "using SQLBackend '%s'", (char *) temp_ptr);
sql_cmdtable = b->cmdtab;
break;
}
}
/* If no match is found, default to using the first entry in
* the list.
*/
if (!sql_cmdtable) {
sql_log(DEBUG_INFO,
"SQLBackend '%s' not found, defaulting to '%s' backend",
(char *) temp_ptr, sql_backends->backend);
sql_cmdtable = sql_backends->cmdtab;
}
} else {
/* Default to using the first entry in the list. */
sql_log(DEBUG_INFO, "defaulting to '%s' backend",
sql_backends->backend);
sql_cmdtable = sql_backends->cmdtab;
}
}
/* Get our backend info and toss it up */
cmd = _sql_make_cmd(tmp_pool, 1, "foo");
mr = _sql_dispatch(cmd, "sql_identify");
_sql_check_response(mr);
sd = (sql_data_t *) mr->data;
sql_log(DEBUG_INFO, "backend module '%s'", sd->data[0]);
sql_log(DEBUG_INFO, "backend api '%s'", sd->data[1]);
SQL_FREE_CMD(cmd);
sql_log(DEBUG_FUNC, "%s", ">>> sql_sess_init");
if (!sql_pool) {
sql_pool = make_sub_pool(session.pool);
pr_pool_tag(sql_pool, MOD_SQL_VERSION);
}
group_name_cache = make_cache(sql_pool, _group_name, _groupcmp);
passwd_name_cache = make_cache(sql_pool, _passwd_name, _passwdcmp);
group_gid_cache = make_cache(sql_pool, _group_gid, _groupcmp);
passwd_uid_cache = make_cache(sql_pool, _passwd_uid, _passwdcmp);
cmap.group_cache_filled = 0;
cmap.passwd_cache_filled = 0;
cmap.curr_group = NULL;
cmap.curr_passwd = NULL;
/* Construct our internal cache structure for this session. */
memset(&cmap, 0, sizeof(cmap));
temp_ptr = get_param_ptr(main_server->conf, "SQLAuthenticate", FALSE);
if (temp_ptr)
cmap.authmask = *((int *) temp_ptr);
else
cmap.authmask = SQL_AUTH_GROUPS|SQL_AUTH_USERS|SQL_AUTH_GROUPSET|
SQL_AUTH_USERSET;
negative_cache = get_param_ptr(main_server->conf, "SQLNegativeCache",
FALSE);
cmap.negative_cache = negative_cache ? *negative_cache : FALSE;
/* SQLHomedirOnDemand defaults to NO */
temp_ptr = get_param_ptr(main_server->conf, "SQLHomedirOnDemand", FALSE);
if (temp_ptr && (*((unsigned char *) temp_ptr) == TRUE))
cmap.buildhomedir = TRUE;
else
cmap.buildhomedir = FALSE;
cmap.defaulthomedir = get_param_ptr(main_server->conf, "SQLDefaultHomedir",
FALSE);
temp_ptr = get_param_ptr(main_server->conf, "SQLUserTable", FALSE);
/* if we have no SQLUserTable, SQLUserInfo was not used -- default all */
if (!temp_ptr) {
cmap.usrtable = MOD_SQL_DEF_USERTABLE;
cmap.usrfield = MOD_SQL_DEF_USERNAMEFIELD;
cmap.pwdfield = MOD_SQL_DEF_USERPASSWORDFIELD;
cmap.uidfield = MOD_SQL_DEF_USERUIDFIELD;
cmap.gidfield = MOD_SQL_DEF_USERGIDFIELD;
cmap.homedirfield = MOD_SQL_DEF_USERHOMEDIRFIELD;
cmap.shellfield = MOD_SQL_DEF_USERSHELLFIELD;
/* It's possible that a custom UserInfo query was configured. Check for
* this.
*/
temp_ptr = get_param_ptr(main_server->conf, "SQLCustomUserInfo", FALSE);
if (temp_ptr) {
config_rec *custom_c = NULL;
char *named_query = pstrcat(tmp_pool, "SQLNamedQuery_", temp_ptr, NULL);
custom_c = find_config(main_server->conf, CONF_PARAM, named_query, FALSE);
if (custom_c == NULL) {
sql_log(DEBUG_INFO, "error: unable to resolve custom "
"SQLNamedQuery name '%s'", temp_ptr);
} else
cmap.usercustom = temp_ptr;
}
} else {
cmap.usrtable = temp_ptr;
cmap.usrfield = get_param_ptr(main_server->conf, "SQLUsernameField", FALSE);
cmap.pwdfield = get_param_ptr(main_server->conf, "SQLPasswordField", FALSE);
cmap.uidfield = get_param_ptr(main_server->conf, "SQLUidField", FALSE);
cmap.gidfield = get_param_ptr(main_server->conf, "SQLGidField", FALSE);
cmap.homedirfield = get_param_ptr(main_server->conf, "SQLHomedirField",
FALSE);
cmap.shellfield = get_param_ptr(main_server->conf, "SQLShellField", FALSE);
}
/* Build the userfieldset */
fieldset = pstrcat(tmp_pool, cmap.usrfield, ", ", cmap.pwdfield, NULL);
if (cmap.uidfield)
fieldset = pstrcat(tmp_pool, fieldset, ", ", cmap.uidfield, NULL);
if (cmap.gidfield)
fieldset = pstrcat(tmp_pool, fieldset, ", ", cmap.gidfield, NULL);
if (cmap.homedirfield)
fieldset = pstrcat(tmp_pool, fieldset, ", ", cmap.homedirfield, NULL);
if (cmap.shellfield)
fieldset = pstrcat(tmp_pool, fieldset, ", ", cmap.shellfield, NULL);
cmap.usrfields = pstrdup(sql_pool, fieldset);
temp_ptr = get_param_ptr(main_server->conf, "SQLGroupTable", FALSE);
/* If we have no temp_ptr, SQLGroupInfo was not used - default all */
if (!temp_ptr) {
cmap.grptable = MOD_SQL_DEF_GROUPTABLE;
cmap.grpfield = MOD_SQL_DEF_GROUPNAMEFIELD;
cmap.grpgidfield = MOD_SQL_DEF_GROUPGIDFIELD;
cmap.grpmembersfield = MOD_SQL_DEF_GROUPMEMBERSFIELD;
} else {
cmap.grptable = get_param_ptr(main_server->conf, "SQLGroupTable", FALSE);
cmap.grpfield = get_param_ptr(main_server->conf, "SQLGroupnameField",
FALSE);
cmap.grpgidfield = get_param_ptr(main_server->conf, "SQLGroupGIDField",
FALSE);
cmap.grpmembersfield = get_param_ptr(main_server->conf,
"SQLGroupMembersField", FALSE);
}
/* Build the groupfieldset */
fieldset = pstrcat(tmp_pool, cmap.grpfield, ", ", cmap.grpgidfield, ", ",
cmap.grpmembersfield, NULL);
cmap.grpfields = pstrdup(sql_pool, fieldset);
temp_ptr = get_param_ptr(main_server->conf, "SQLUserWhereClause", FALSE);
cmap.userwhere = temp_ptr ? pstrcat(sql_pool, "(", temp_ptr, ")", NULL) : "";
temp_ptr = get_param_ptr(main_server->conf, "SQLGroupWhereClause", FALSE);
cmap.groupwhere = temp_ptr ? pstrcat(sql_pool, "(", temp_ptr, ")", NULL) : "";
temp_ptr = get_param_ptr(main_server->conf, "SQLAuthTypes", FALSE);
cmap.authlist = temp_ptr;
if (!cmap.authlist)
sql_log(DEBUG_INFO, "%s", "error: no SQLAuthTypes configured");
temp_ptr = get_param_ptr(main_server->conf, "SQLMinID", FALSE);
if (temp_ptr) {
cmap.minuseruid = *((unsigned long *) temp_ptr);
cmap.minusergid = *((unsigned long *) temp_ptr);
} else {
temp_ptr = get_param_ptr(main_server->conf, "SQLMinUserUID", FALSE);
cmap.minuseruid = temp_ptr ? *((uid_t *) temp_ptr) : MOD_SQL_MIN_USER_UID;
temp_ptr = get_param_ptr(main_server->conf, "SQLMinUserGID", FALSE);
cmap.minusergid = temp_ptr ? *((gid_t *) temp_ptr) : MOD_SQL_MIN_USER_GID;
}
temp_ptr = get_param_ptr(main_server->conf, "SQLDefaultUID", FALSE);
cmap.defaultuid = temp_ptr ? *((uid_t *) temp_ptr) : MOD_SQL_DEF_UID;
temp_ptr = get_param_ptr(main_server->conf, "SQLDefaultGID", FALSE);
cmap.defaultgid = temp_ptr ? *((gid_t *) temp_ptr) : MOD_SQL_DEF_GID;
c = find_config(main_server->conf, CONF_PARAM, "SQLRatioStats", FALSE);
if (c) {
cmap.sql_fstor = c->argv[0];
cmap.sql_fretr = c->argv[1];
cmap.sql_bstor = c->argv[2];
cmap.sql_bretr = c->argv[3];
}
c = find_config(main_server->conf, CONF_PARAM, "SQLRatios", FALSE);
if (c) {
if (!cmap.sql_fstor) {
pr_log_pri(PR_LOG_WARNING,
MOD_SQL_VERSION ": warning: SQLRatios directive ineffective "
"without SQLRatioStats on");
sql_log(DEBUG_WARN, "%s", "warning: SQLRatios directive ineffective "
"without SQLRatioStats on");
}
cmap.sql_frate = c->argv[0];
cmap.sql_fcred = c->argv[1];
cmap.sql_brate = c->argv[2];
cmap.sql_bcred = c->argv[3];
}
if (!cmap.homedirfield &&
!cmap.defaulthomedir) {
cmap.authmask ^= SQL_AUTH_USERS;
pr_log_pri(PR_LOG_WARNING, MOD_SQL_VERSION
": warning: no homedir field and no default specified. "
"User authentication is OFF");
sql_log(DEBUG_WARN, "%s",
"warning: no homedir field and no default specified. "
"User authentication is OFF");
}
c = find_config(main_server->conf, CONF_PARAM, "SQLConnectInfo", FALSE);
if (!c) {
cmap.authmask = 0;
cmap.engine = 0;
cmap.sql_fstor = NULL;
cmap.sql_frate = NULL;
sql_log(DEBUG_WARN, "%s",
"warning: no SQLConnectInfo specified. mod_sql is OFF");
} else {
if (strcasecmp(c->argv[3], "percall") == 0)
percall = 1;
cmd = _sql_make_cmd(tmp_pool, 5, "default", c->argv[1], c->argv[2],
c->argv[0], c->argv[3]);
mr = _sql_dispatch(cmd,"sql_defineconnection");
_sql_check_response(mr);
SQL_FREE_CMD(cmd);
if (!percall) {
cmd = _sql_make_cmd(tmp_pool, 1, "default");
mr = _sql_dispatch(cmd, "sql_open");
_sql_check_response(mr);
SQL_FREE_CMD(cmd);
sql_log(DEBUG_INFO, "%s", "backend successfully connected.");
} else {
sql_log(DEBUG_INFO, "%s",
"backend will not be checked until first use.");
}
cmap.engine = SQL_ENGINE_FL_AUTH|SQL_ENGINE_FL_LOG;
}
sql_log(DEBUG_INFO, "mod_sql engine : %s", cmap.engine ? "on" : "off");
sql_log(DEBUG_INFO, "negative_cache : %s",
cmap.negative_cache ? "on" : "off");
authstr = "";
if (SQL_USERS)
authstr = pstrcat(tmp_pool, authstr, "users ", NULL);
if (SQL_GROUPS)
authstr = pstrcat(tmp_pool, authstr, "groups ", NULL);
if (SQL_USERSET) {
if (SQL_FASTUSERS)
authstr = pstrcat(tmp_pool, authstr, "userset(fast) ", NULL);
else
authstr = pstrcat(tmp_pool, authstr, "userset ", NULL);
}
if (SQL_GROUPSET) {
if (SQL_FASTGROUPS)
authstr = pstrcat(tmp_pool, authstr, "groupset(fast)", NULL);
else
authstr = pstrcat(tmp_pool, authstr, "groupset", NULL);
}
sql_log(DEBUG_INFO, "authenticate : %s",
(!authstr || *authstr=='\0') ? "off" : authstr);
if (SQL_USERS || cmap.sql_fstor || cmap.sql_frate) {
sql_log(DEBUG_INFO, "usertable : %s", cmap.usrtable);
sql_log(DEBUG_INFO, "userid field : %s", cmap.usrfield);
}
if (SQL_USERS) {
sql_log(DEBUG_INFO, "password field : %s", cmap.pwdfield);
sql_log(DEBUG_INFO, "uid field : %s",
(cmap.uidfield ? cmap.uidfield : "NULL"));
sql_log(DEBUG_INFO, "gid field : %s",
(cmap.gidfield ? cmap.gidfield : "NULL"));
if (cmap.homedirfield)
sql_log(DEBUG_INFO, "homedir field : %s", cmap.homedirfield);
if (cmap.defaulthomedir)
sql_log(DEBUG_INFO, "homedir(default) : '%s'", cmap.defaulthomedir);
sql_log(DEBUG_INFO, "shell field : %s",
(cmap.shellfield ? cmap.shellfield : "NULL"));
sql_log(DEBUG_INFO, "homedirondemand : %s",
(cmap.buildhomedir ? "true" : "false"));
}
if (SQL_GROUPS) {
sql_log(DEBUG_INFO, "group table : %s", cmap.grptable);
sql_log(DEBUG_INFO, "groupname field : %s", cmap.grpfield);
sql_log(DEBUG_INFO, "grp gid field : %s", cmap.grpgidfield);
sql_log(DEBUG_INFO, "grp members field : %s", cmap.grpmembersfield);
}
if (SQL_USERS) {
sql_log(DEBUG_INFO, "SQLMinUserUID : %u", cmap.minuseruid);
sql_log(DEBUG_INFO, "SQLMinUserGID : %u", cmap.minusergid);
}
if (SQL_GROUPS) {
sql_log(DEBUG_INFO, "SQLDefaultUID : %u", cmap.defaultuid);
sql_log(DEBUG_INFO, "SQLDefaultGID : %u", cmap.defaultgid);
}
if (cmap.sql_fstor) {
sql_log(DEBUG_INFO, "sql_fstor : %s", cmap.sql_fstor);
sql_log(DEBUG_INFO, "sql_fretr : %s", cmap.sql_fretr);
sql_log(DEBUG_INFO, "sql_bstor : %s", cmap.sql_bstor);
sql_log(DEBUG_INFO, "sql_bretr : %s", cmap.sql_bretr);
}
if (cmap.sql_frate) {
sql_log(DEBUG_INFO, "sql_frate : %s", cmap.sql_frate);
sql_log(DEBUG_INFO, "sql_fcred : %s", cmap.sql_fcred);
sql_log(DEBUG_INFO, "sql_brate : %s", cmap.sql_brate);
sql_log(DEBUG_INFO, "sql_bcred : %s", cmap.sql_bcred);
}
sql_log(DEBUG_FUNC, "%s", "<<< sql_sess_init");
destroy_pool(tmp_pool);
/* Add our exit handler */
pr_event_register(&sql_module, "core.exit", sql_exit_ev, NULL);
return 0;
}
/*****************************************************************
*
* HANDLER TABLES
*
*****************************************************************/
static conftable sql_conftab[] = {
{ "SQLAuthenticate", set_sqlauthenticate, NULL },
{ "SQLAuthTypes", set_sqlauthtypes, NULL },
{ "SQLBackend", set_sqlbackend, NULL },
{ "SQLConnectInfo", set_sqlconnectinfo, NULL },
{ "SQLEngine", set_sqlengine, NULL },
{ "SQLUserInfo", set_sqluserinfo, NULL},
{ "SQLUserWhereClause", set_sqluserwhereclause, NULL },
{ "SQLGroupInfo", set_sqlgroupinfo, NULL },
{ "SQLGroupWhereClause", set_sqlgroupwhereclause, NULL },
{ "SQLMinID", set_sqlminid, NULL },
{ "SQLMinUserUID", set_sqlminuseruid, NULL },
{ "SQLMinUserGID", set_sqlminusergid, NULL },
{ "SQLDefaultUID", set_sqldefaultuid, NULL },
{ "SQLDefaultGID", set_sqldefaultgid, NULL },
{ "SQLNegativeCache", set_sqlnegativecache, NULL },
{ "SQLRatios", set_sqlratios, NULL },
{ "SQLRatioStats", set_sqlratiostats, NULL },
{ "SQLDefaultHomedir", set_sqldefaulthomedir, NULL },
{ "SQLHomedirOnDemand", set_sqlhomedirondemand, NULL },
{ "SQLLog", set_sqllog, NULL },
{ "SQLLogFile", set_sqllogfile, NULL },
{ "SQLNamedQuery", set_sqlnamedquery, NULL },
{ "SQLShowInfo", set_sqlshowinfo, NULL },
{ NULL, NULL, NULL }
};
static cmdtable sql_cmdtab[] = {
{ PRE_CMD, C_QUIT, G_NONE, log_master, FALSE, FALSE },
{ PRE_CMD, C_PASS, G_NONE, sql_pre_pass, FALSE, FALSE },
{ POST_CMD, C_RETR, G_NONE, sql_post_retr, FALSE, FALSE },
{ POST_CMD, C_STOR, G_NONE, sql_post_stor, FALSE, FALSE },
{ POST_CMD, C_ANY, G_NONE, info_master, FALSE, FALSE },
{ POST_CMD_ERR, C_ANY, G_NONE, errinfo_master, FALSE, FALSE },
{ LOG_CMD, C_ANY, G_NONE, log_master, FALSE, FALSE },
{ LOG_CMD_ERR, C_ANY, G_NONE, err_master, FALSE, FALSE },
/* Module hooks */
{ HOOK, "sql_change", G_NONE, sql_change, FALSE, FALSE },
{ HOOK, "sql_escapestr",G_NONE, sql_escapestr, FALSE, FALSE },
{ HOOK, "sql_lookup", G_NONE, sql_lookup, FALSE, FALSE },
{ 0, NULL }
};
static authtable sql_authtab[] = {
{ 0, "setpwent", cmd_setpwent },
{ 0, "getpwent", cmd_getpwent },
{ 0, "endpwent", cmd_endpwent },
{ 0, "setgrent", cmd_setgrent },
{ 0, "getgrent", cmd_getgrent },
{ 0, "endgrent", cmd_endgrent },
{ 0, "getpwnam", cmd_getpwnam },
{ 0, "getpwuid", cmd_getpwuid },
{ 0, "getgrnam", cmd_getgrnam },
{ 0, "getgrgid", cmd_getgrgid },
{ 0, "auth", cmd_auth },
{ 0, "check", cmd_check },
{ 0, "uid2name", cmd_uid2name },
{ 0, "gid2name", cmd_gid2name },
{ 0, "name2uid", cmd_name2uid },
{ 0, "name2gid", cmd_name2gid },
{ 0, "getgroups", cmd_getgroups },
/* Note: these should be HOOKs, and in the cmdtab. */
{ 0, "getstats", cmd_getstats },
{ 0, "getratio", cmd_getratio },
{ 0, NULL, NULL }
};
module sql_module = {
/* Always NULL */
NULL, NULL,
/* Module API version */
0x20,
/* Module name */
"sql",
/* Module configuration directive table */
sql_conftab,
/* Module command handler table */
sql_cmdtab,
/* Module auth handler table */
sql_authtab,
/* Module initialization */
sql_init,
/* Session initialization */
sql_sess_init,
/* Module version */
MOD_SQL_VERSION
};
Last Updated: Thu Feb 23 11:06:38 2006
HTML generated by tj's src2html script