Logo Search packages:      
Sourcecode: cyrus-sasl2 version File versions  Download package

sql.c

/*
**
** SQL Auxprop plugin
**
** Ken Murchison
** Maya Nigrosh -- original store() and txn support
** Simon Loader -- original mysql plugin
** Patrick Welche -- original pgsql plugin
**
** $Id: sql.c,v 1.27 2004/06/30 19:31:11 rjs3 Exp $
**
*/

#include <config.h>

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>

#include "sasl.h"
#include "saslutil.h"
#include "saslplug.h"

#include <ctype.h>

#include "plugin_common.h"

#define sql_max(a, b) ((a) > (b) ? (a) : (b))
#define sql_len(input) ((input) ? strlen(input) : 0)
#define sql_exists(input) ((input) && (*input))

typedef struct sql_engine {
    const char *name;
    void *(*sql_open)(char *host, char *port, int usessl,
                  const char *user, const char *password,
                  const char *database, const sasl_utils_t *utils);
    int (*sql_escape_str)(char *to, const char *from);
    int (*sql_begin_txn)(void *conn, const sasl_utils_t *utils);
    int (*sql_commit_txn)(void *conn, const sasl_utils_t *utils);
    int (*sql_rollback_txn)(void *conn, const sasl_utils_t *utils);
    int (*sql_exec)(void *conn, const char *cmd, char *value, size_t size,
                size_t *value_len, const sasl_utils_t *utils);
    void (*sql_close)(void *conn);
} sql_engine_t;

typedef struct sql_settings {
    const sql_engine_t *sql_engine;
    const char *sql_user;
    const char *sql_passwd;
    const char *sql_hostnames;
    const char *sql_database;
    const char *sql_select;
    const char *sql_insert;
    const char *sql_update;
    int sql_usessl;
} sql_settings_t;

static const char * SQL_BLANK_STRING = "";
static const char * SQL_WILDCARD = "*";
static const char * SQL_NULL_VALUE = "NULL";


#ifdef HAVE_MYSQL
#include <mysql.h>

static void *_mysql_open(char *host, char *port, int usessl,
                   const char *user, const char *password,
                   const char *database, const sasl_utils_t *utils)
{
    MYSQL *mysql;
    
    if (!(mysql = mysql_init(NULL))) {
      utils->log(NULL, SASL_LOG_ERR,
               "sql plugin: could not execute mysql_init()");
      return NULL;
    }
    
    return mysql_real_connect(mysql, host, user, password, database,
                        port ? strtoul(port, NULL, 10) : 0, NULL,
                        usessl ? CLIENT_SSL : 0);
}

static int _mysql_escape_str(char *to, const char *from)
{
    return mysql_escape_string(to, from, strlen(from));
}

static int _mysql_exec(void *conn, const char *cmd, char *value, size_t size,
                   size_t *value_len, const sasl_utils_t *utils)
{
    MYSQL_RES *result;
    MYSQL_ROW row;
    int row_count, len;
    
    len = strlen(cmd);
    /* mysql_real_query() doesn't want a terminating ';' */
    if (cmd[len-1] == ';') len--;

    /* run the query */
    if ((mysql_real_query(conn, cmd, len) < 0)) {
      utils->log(NULL, SASL_LOG_ERR, "sql query failed: %s",
               mysql_error(conn));
      return -1;
    }

    /* see if we should expect some results */
    if (!mysql_field_count(conn)) {
      /* no results (BEGIN, COMMIT, DELETE, INSERT, UPDATE) */
      return 0;
    }

    /* get the results */
    result = mysql_store_result(conn);
    if (!result) {
      /* umm nothing found */
      utils->log(NULL, SASL_LOG_NOTE, "sql plugin: no result found");
      return -1;
    }

    /* quick row check */
    row_count = mysql_num_rows(result);
    if (!row_count) {
      /* umm nothing found */
      mysql_free_result(result);
      utils->log(NULL, SASL_LOG_NOTE, "sql plugin: no result found");
      return -1;
    }
    if (row_count > 1) {
      utils->log(NULL, SASL_LOG_WARN,
               "sql plugin: found duplicate row for query %s", cmd);
    }
    
    /* now get the result set value and value_len */
    /* we only fetch one because we don't care about the rest */
    row = mysql_fetch_row(result);
    if (!row || !row[0]) {
      /* umm nothing found */
      utils->log(NULL, SASL_LOG_NOTE, "sql plugin: no result found");
      mysql_free_result(result);
      return -1;
    }
    if (value) {
      strncpy(value, row[0], size-2);
      value[size-1] = '\0';
      if (value_len) *value_len = strlen(value);
    }
    
    /* free result */
    mysql_free_result(result);
    
    return 0;
}

static int _mysql_begin_txn(void *conn, const sasl_utils_t *utils)
{
    return _mysql_exec(conn,
#if MYSQL_VERSION_ID >= 40011
                   "START TRANSACTION",
#else
                   "BEGIN",
#endif
                   NULL, 0, NULL, utils);
}

static int _mysql_commit_txn(void *conn, const sasl_utils_t *utils)
{
    return _mysql_exec(conn, "COMMIT", NULL, 0, NULL, utils);
}

static int _mysql_rollback_txn(void *conn, const sasl_utils_t *utils)
{
    return _mysql_exec(conn, "ROLLBACK", NULL, 0, NULL, utils);
}

static void _mysql_close(void *conn)
{
    mysql_close(conn);
}
#endif /* HAVE_MYSQL */

#ifdef HAVE_PGSQL
#include <libpq-fe.h>

static void *_pgsql_open(char *host, char *port, int usessl,
                   const char *user, const char *password,
                   const char *database, const sasl_utils_t *utils)
{
    PGconn *conn = NULL;
    char *conninfo, *sep;
    
    /* create the connection info string */
    /* The 64 represents the number of characters taken by
     * the keyword tokens, plus a small pad
     */
    conninfo = utils->malloc(64 + sql_len(host) + sql_len(port)
                       + sql_len(user) + sql_len(password)
                       + sql_len(database));
    if (!conninfo) {
      MEMERROR(utils);
      return NULL;
    }
    
    /* add each term that exists */
    conninfo[0] = '\0';
    sep = "";
    if (sql_exists(host)) {
      strcat(conninfo, sep);
      strcat(conninfo, "host='");
      strcat(conninfo, host);
      strcat(conninfo, "'");
      sep = " ";
    }
    if (sql_exists(port)) {
      strcat(conninfo, sep);
      strcat(conninfo, "port='");
      strcat(conninfo, port);
      strcat(conninfo, "'");
      sep = " ";
    }
    if (sql_exists(user)) {
      strcat(conninfo, sep);
      strcat(conninfo, "user='");
      strcat(conninfo, user);
      strcat(conninfo, "'");
      sep = " ";
    }
    if (sql_exists(password)) {
      strcat(conninfo, sep);
      strcat(conninfo, "password='");
      strcat(conninfo, password);
      strcat(conninfo, "'");
      sep = " ";
    }
    if (sql_exists(database)) {
      strcat(conninfo, sep);
      strcat(conninfo, "dbname='");
      strcat(conninfo, database);
      strcat(conninfo, "'");
      sep = " ";
    }
    if (usessl) {
      strcat(conninfo, sep);
      strcat(conninfo, "requiressl='1'");
    }
    
    conn = PQconnectdb(conninfo);
    free(conninfo);
    
    if ((PQstatus(conn) != CONNECTION_OK)) {
      utils->log(NULL, SASL_LOG_ERR, "sql plugin: %s", PQerrorMessage(conn));
      return NULL;
    }
    
    return conn;
}

static int _pgsql_escape_str(char *to, const char *from)
{
    return PQescapeString(to, from, strlen(from));
}

static int _pgsql_exec(void *conn, const char *cmd, char *value, size_t size,
                   size_t *value_len, const sasl_utils_t *utils)
{
    PGresult *result;
    int row_count;
    ExecStatusType status;
    
    /* run the query */
    result = PQexec(conn, cmd);
    
    /* check the status */
    status = PQresultStatus(result);
    if (status == PGRES_COMMAND_OK) {
      /* no results (BEGIN, COMMIT, DELETE, INSERT, UPDATE) */
      PQclear(result);
      return 0;
    }
    else if (status != PGRES_TUPLES_OK) {
      /* error */
      utils->log(NULL, SASL_LOG_DEBUG, "sql plugin: %s ",
               PQresStatus(status));
      PQclear(result);
      return -1;
    }
    
    /* quick row check */
    row_count = PQntuples(result);
    if (!row_count) {
      /* umm nothing found */
      utils->log(NULL, SASL_LOG_NOTE, "sql plugin: no result found");
      PQclear(result);
      return -1;
    }
    if (row_count > 1) {
      utils->log(NULL, SASL_LOG_WARN,
               "sql plugin: found duplicate row for query %s", cmd);
    }
    
    /* now get the result set value and value_len */
    /* we only fetch one because we don't care about the rest */
    if (value) {
      strncpy(value, PQgetvalue(result,0,0), size-2);
      value[size-1] = '\0';
      if (value_len) *value_len = strlen(value);
    }
    
    /* free result */
    PQclear(result);
    return 0;
}

static int _pgsql_begin_txn(void *conn, const sasl_utils_t *utils)
{
    return _pgsql_exec(conn, "BEGIN;", NULL, 0, NULL, utils);
}

static int _pgsql_commit_txn(void *conn, const sasl_utils_t *utils)
{
    return _pgsql_exec(conn, "COMMIT;", NULL, 0, NULL, utils);
}

static int _pgsql_rollback_txn(void *conn, const sasl_utils_t *utils)
{
    return _pgsql_exec(conn, "ROLLBACK;", NULL, 0, NULL, utils);
}

static void _pgsql_close(void *conn)
{
    PQfinish(conn);
}
#endif /* HAVE_PGSQL */

#ifdef HAVE_SQLITE
#include <sqlite.h>

static void *_sqlite_open(char *host, char *port, int usessl,
                    const char *user, const char *password,
                    const char *database, const sasl_utils_t *utils)
{
    int rc;
    sqlite *db;
    char *zErrMsg = NULL;

    db = sqlite_open(database, 0, &zErrMsg);
    if (db == NULL) {
      utils->log(NULL, SASL_LOG_ERR, "sql plugin: %s", zErrMsg);
      sqlite_freemem (zErrMsg);
      return NULL;
    }

    rc = sqlite_exec(db, "PRAGMA empty_result_callbacks = ON", NULL, NULL, &zErrMsg);
    if (rc != SQLITE_OK) {
      utils->log(NULL, SASL_LOG_ERR, "sql plugin: %s", zErrMsg);
      sqlite_freemem (zErrMsg);
      sqlite_close(db);
      return NULL;
    }

    return (void*)db;
}

static int _sqlite_escape_str(char *to, const char *from)
{
    char s;

    while ( (s = *from++) != '\0' ) {
      if (s == '\'' || s == '\\') {
          *to++ = '\\';
      }
      *to++ = s;
    }
    *to = '\0';

    return 0;
}

static int sqlite_my_callback(void *pArg, int argc, char **argv,
                        char **columnNames)
{
    char **result = (char**)pArg;

    if (argv == NULL) {
      *result = NULL;                     /* no record */
    } else if (argv[0] == NULL) {
      *result = strdup(SQL_NULL_VALUE);   /* NULL IS SQL_NULL_VALUE */
    } else {
      *result = strdup(argv[0]);
    }

    return /*ABORT*/1;
}

static int _sqlite_exec(void *db, const char *cmd, char *value, size_t size,
                    size_t *value_len, const sasl_utils_t *utils)
{
    int rc;
    char *result = NULL;
    char *zErrMsg = NULL;

    rc = sqlite_exec((sqlite*)db, cmd, sqlite_my_callback, (void*)&result, &zErrMsg);
    if (rc != SQLITE_OK && rc != SQLITE_ABORT) {
      utils->log(NULL, SASL_LOG_DEBUG, "sql plugin: %s ", zErrMsg);
      sqlite_freemem (zErrMsg);
      return -1;
    }

    if (rc == SQLITE_OK) {
      /* no results (BEGIN, COMMIT, DELETE, INSERT, UPDATE) */
      return 0;
    }

    if (result == NULL) {
      /* umm nothing found */
      utils->log(NULL, SASL_LOG_NOTE, "sql plugin: no result found");
      return -1;
    }

    /* XXX: Duplication cannot be found by this method. */

    /* now get the result set value and value_len */
    /* we only fetch one because we don't care about the rest */
    if (value) {
      strncpy(value, result, size - 2);
      value[size - 1] = '\0';
      if (value_len) {
          *value_len = strlen(value);
      }
    }

    /* free result */
    free(result);
    return 0;
}

static int _sqlite_begin_txn(void *db, const sasl_utils_t *utils)
{
    return _sqlite_exec(db, "BEGIN TRANSACTION", NULL, 0, NULL, utils);
}

static int _sqlite_commit_txn(void *db, const sasl_utils_t *utils)
{
    return _sqlite_exec(db, "COMMIT TRANSACTION", NULL, 0, NULL, utils);
}

static int _sqlite_rollback_txn(void *db, const sasl_utils_t *utils)
{
    return _sqlite_exec(db, "ROLLBACK TRANSACTION", NULL, 0, NULL, utils);
}

static void _sqlite_close(void *db)
{
    sqlite_close((sqlite*)db);
}
#endif /* HAVE_SQLITE */

static const sql_engine_t sql_engines[] = {
#ifdef HAVE_MYSQL
    { "mysql", &_mysql_open, &_mysql_escape_str,
      &_mysql_begin_txn, &_mysql_commit_txn, &_mysql_rollback_txn,
      &_mysql_exec, &_mysql_close },
#endif /* HAVE_MYSQL */
#ifdef HAVE_PGSQL
    { "pgsql", &_pgsql_open, &_pgsql_escape_str,
      &_pgsql_begin_txn, &_pgsql_commit_txn, &_pgsql_rollback_txn,
      &_pgsql_exec, &_pgsql_close },
#endif
#ifdef HAVE_SQLITE
    { "sqlite", &_sqlite_open, &_sqlite_escape_str,
      &_sqlite_begin_txn, &_sqlite_commit_txn, &_sqlite_rollback_txn,
      &_sqlite_exec, &_sqlite_close },
#endif
    { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
};

/*
**  Sql_create_statement
**   uses statement line and allocate memory to replace
**  Parts with the strings provided.
**   %<char> =  no change
**   %% = %
**   %u = user
**   %p = prop
**   %r = realm
**   %v = value of prop
**  e.g select %p from auth where user = %p and domain = %r;
**  Note: calling function must free memory.
**
*/

static char *sql_create_statement(const char *statement, const char *prop,
                          const char *user, const char *realm, 
                          const char *value,  
                          const sasl_utils_t *utils)
{
    const char *ptr, *line_ptr;
    char *buf, *buf_ptr;
    int filtersize;
    int ulen, plen, rlen, vlen;
    int numpercents=0;
    int biggest;
    size_t i;
    
    /* calculate memory needed for creating the complete query string. */
    ulen = strlen(user);
    rlen = strlen(realm);
    plen = strlen(prop);
    vlen = sql_len(value);
    
    /* what if we have multiple %foo occurrences in the input query? */
    for (i = 0; i < strlen(statement); i++) {
      if (statement[i] == '%') {
          numpercents++;
      }
    }
    
    /* find the biggest of ulen, rlen, plen, vlen */
    biggest = sql_max(sql_max(ulen, rlen), sql_max(plen, vlen));
    
    /* plus one for the semicolon...and don't forget the trailing 0x0 */
    filtersize = strlen(statement) + 1 + (numpercents*biggest)+1;
    
    /* ok, now try to allocate a chunk of that size */
    buf = (char *) utils->malloc(filtersize);
    
    if (!buf) {
      MEMERROR(utils);
      return NULL;
    }
    
    buf_ptr = buf;
    line_ptr = statement;
    
    /* replace the strings */
    while ( (ptr = strchr(line_ptr, '%')) ) {
      /* copy up to but not including the next % */
      memcpy(buf_ptr, line_ptr, ptr - line_ptr); 
      buf_ptr += ptr - line_ptr;
      ptr++;
      switch (ptr[0]) {
      case '%':
          buf_ptr[0] = '%';
          buf_ptr++;
          break;
      case 'u':
          memcpy(buf_ptr, user, ulen);
          buf_ptr += ulen;
          break;
      case 'r':
          memcpy(buf_ptr, realm, rlen);
          buf_ptr += rlen;
          break;
      case 'p':
          memcpy(buf_ptr, prop, plen);
          buf_ptr += plen;
          break;
      case 'v':
          if (value != NULL) {
            memcpy(buf_ptr, value, vlen);
            buf_ptr += vlen;
          }
          else {
            utils->log(NULL, SASL_LOG_ERR,
                     "'%%v' shouldn't be in a SELECT or DELETE");
          }
          break;
      default:
          buf_ptr[0] = '%';
          buf_ptr[1] = ptr[0];
          buf_ptr += 2;
          break;
      }
      ptr++;
      line_ptr = ptr;
    }
    
    memcpy(buf_ptr, line_ptr, strlen(line_ptr)+1);
    /* Make sure the current portion of the statement ends with a semicolon */
    if (buf_ptr[strlen(buf_ptr-1)] != ';') {
      strcat(buf_ptr, ";");
    }

    return (buf);
}

/* sql_get_settings
 *
 * Get the auxprop settings and put them in the global context array
*/
static void sql_get_settings(const sasl_utils_t *utils, void *glob_context)
{
    sql_settings_t *settings;
    int r;
    const char *usessl, *engine_name;
    const sql_engine_t *e;
    
    settings = (sql_settings_t *) glob_context;
    
    r = utils->getopt(utils->getopt_context,"SQL", "sql_engine",
                  &engine_name, NULL);
    if (r || !engine_name) {
      engine_name = "mysql";
    }
    
    /* find the correct engine */
    e = sql_engines;
    while (e->name) {
      if (!strcasecmp(engine_name, e->name)) break;
      e++;
    }

    if (!e->name) {
      utils->log(NULL, SASL_LOG_ERR, "SQL engine '%s' not supported",
               engine_name);
    }

    settings->sql_engine = e;

    r = utils->getopt(utils->getopt_context,"SQL","sql_user",
                  &settings->sql_user, NULL);
    if ( r || !settings->sql_user ) {
      settings->sql_user = SQL_BLANK_STRING;
    }

    r = utils->getopt(utils->getopt_context,"SQL", "sql_passwd",
                  &settings->sql_passwd, NULL);
    if (r || !settings->sql_passwd ) {
      settings->sql_passwd = SQL_BLANK_STRING;
    }

    r = utils->getopt(utils->getopt_context,"SQL", "sql_hostnames",
                  &settings->sql_hostnames, NULL);
    if (r || !settings->sql_hostnames ) {
      settings->sql_hostnames = SQL_BLANK_STRING;
    }

    r = utils->getopt(utils->getopt_context,"SQL", "sql_database",
                  &settings->sql_database, NULL);
    if (r || !settings->sql_database ) {
      settings->sql_database = SQL_BLANK_STRING;
    }

    r = utils->getopt(utils->getopt_context,"SQL", "sql_select",
                  &settings->sql_select, NULL);
    if (r || !settings->sql_select ) {
      /* backwards compatibility */
      r = utils->getopt(utils->getopt_context,"SQL", "sql_statement",
                    &settings->sql_select, NULL);
      if (r || !settings->sql_select) {
          settings->sql_select = SQL_BLANK_STRING;
      }
    }

    r = utils->getopt(utils->getopt_context, "SQL", "sql_insert",
                  &settings->sql_insert, NULL);
    if (r || !settings->sql_insert) {
      settings->sql_insert = SQL_BLANK_STRING;
    }

    r = utils->getopt(utils->getopt_context, "SQL", "sql_update",
                  &settings->sql_update, NULL);
    if (r || !settings->sql_update) {
      settings->sql_update = SQL_BLANK_STRING;
    }

    r = utils->getopt(utils->getopt_context, "SQL", "sql_usessl",
              &usessl, NULL);
    if (r || !usessl) usessl = "no";

    if (*usessl == '1' || *usessl == 'y'  || *usessl == 't' ||
      (*usessl == 'o' && usessl[1] == 'n')) {
      settings->sql_usessl = 1;
    } else {
      settings->sql_usessl = 0;
    }
}

static void *sql_connect(sql_settings_t *settings, const sasl_utils_t *utils)
{
    void *conn = NULL;
    char *db_host_ptr = NULL;
    char *db_host = NULL;
    char *cur_host, *cur_port;

    /* loop around hostnames till we get a connection 
     * it should probably save the connection but for 
     * now we will just disconnect everytime
     */
    utils->log(NULL, SASL_LOG_DEBUG,
             "sql plugin try and connect to a host\n");
    
    /* create a working version of the hostnames */
    _plug_strdup(utils, settings->sql_hostnames, &db_host_ptr, NULL);
    db_host = db_host_ptr;
    cur_host = db_host;
    while (cur_host != NULL) {
      db_host = strchr(db_host,',');
      if (db_host != NULL) {  
          db_host[0] = '\0';

          /* loop till we find some text */
          while (!isalnum(db_host[0])) db_host++;
      }
      
      utils->log(NULL, SASL_LOG_DEBUG,
               "sql plugin trying to open db '%s' on host '%s'%s\n",
               settings->sql_database, cur_host,
               settings->sql_usessl ? " using SSL" : "");
      
      /* set the optional port */
      if ((cur_port = strchr(cur_host, ':'))) *cur_port++ = '\0';
      
      conn = settings->sql_engine->sql_open(cur_host, cur_port,
                                    settings->sql_usessl,
                                    settings->sql_user,
                                    settings->sql_passwd,
                                    settings->sql_database,
                                    utils);
      if (conn) break;
      
      utils->log(NULL, SASL_LOG_ERR,
               "sql plugin could not connect to host %s", cur_host);
      
      cur_host = db_host;
    }

    if (db_host_ptr) utils->free(db_host_ptr);

    return conn;
}

static void sql_auxprop_lookup(void *glob_context,
                         sasl_server_params_t *sparams,
                         unsigned flags,
                         const char *user,
                         unsigned ulen) 
{
    char *userid = NULL;
    /* realm could be used for something clever */
    char *realm = NULL;
    const char *user_realm = NULL;
    const struct propval *to_fetch, *cur;
    char value[8192];
    size_t value_len;
    
    char *user_buf;
    char *query = NULL;
    char *escap_userid = NULL;
    char *escap_realm = NULL;
    sql_settings_t *settings;
    void *conn = NULL;
    int do_txn = 0;
    
    if (!glob_context || !sparams || !user) return;
    
    /* setup the settings */
    settings = (sql_settings_t *) glob_context;
    
    sparams->utils->log(NULL, SASL_LOG_DEBUG,
                  "sql plugin Parse the username %s\n", user);
    
    user_buf = sparams->utils->malloc(ulen + 1);
    if (!user_buf) goto done;
    
    memcpy(user_buf, user, ulen);
    user_buf[ulen] = '\0';
    
    if(sparams->user_realm) {
      user_realm = sparams->user_realm;
    } else {
      user_realm = sparams->serverFQDN;
    }
    
    if (_plug_parseuser(sparams->utils, &userid, &realm, user_realm,
                  sparams->serverFQDN, user_buf) != SASL_OK )
      goto done;
    
    /* just need to escape userid and realm now */
    /* allocate some memory */
    escap_userid = (char *)sparams->utils->malloc(strlen(userid)*2+1);
    escap_realm = (char *)sparams->utils->malloc(strlen(realm)*2+1);
    
    if (!escap_userid || !escap_realm) {
      MEMERROR(sparams->utils);
      goto done;
    }
    
    /*************************************/
    
    /* find out what we need to get */
    /* this corrupts const char *user */
    to_fetch = sparams->utils->prop_get(sparams->propctx);
    if (!to_fetch) goto done;

    conn = sql_connect(settings, sparams->utils);
    if (!conn) {
      sparams->utils->log(NULL, SASL_LOG_ERR,
                      "sql plugin couldn't connect to any host\n");
      
      goto done;
    }
    
    /* escape out */
    settings->sql_engine->sql_escape_str(escap_userid, userid);
    settings->sql_engine->sql_escape_str(escap_realm, realm);
    
    for (cur = to_fetch; cur->name; cur++) {
      char *realname = (char *) cur->name;

      /* Only look up properties that apply to this lookup! */
      if (cur->name[0] == '*'
          && (flags & SASL_AUXPROP_AUTHZID))
          continue;
      if (!(flags & SASL_AUXPROP_AUTHZID)) {
          if(cur->name[0] != '*')
            continue;
          else
            realname = (char*)cur->name + 1;
      }
      
      /* If it's there already, we want to see if it needs to be
       * overridden */
      if (cur->values && !(flags & SASL_AUXPROP_OVERRIDE))
          continue;
      else if (cur->values)
          sparams->utils->prop_erase(sparams->propctx, cur->name);

      if (!do_txn) {
          do_txn = 1;
          sparams->utils->log(NULL, SASL_LOG_DEBUG, "begin transaction");
          if (settings->sql_engine->sql_begin_txn(conn, sparams->utils)) {
            sparams->utils->log(NULL, SASL_LOG_ERR, 
                            "Unable to begin transaction\n");
          }
      }
    
      sparams->utils->log(NULL, SASL_LOG_DEBUG,
                      "sql plugin create statement from %s %s %s\n",
                      realname, escap_userid, escap_realm);
      
      /* create a statement that we will use */
      query = sql_create_statement(settings->sql_select,
                             realname,escap_userid,
                             escap_realm, NULL,
                             sparams->utils);
      
      sparams->utils->log(NULL, SASL_LOG_DEBUG,
                      "sql plugin doing query %s\n", query);
      
      /* run the query */
      if (!settings->sql_engine->sql_exec(conn, query, value, sizeof(value),
                                  &value_len, sparams->utils)) {
          sparams->utils->prop_set(sparams->propctx, cur->name,
                             value, value_len);
      }
      
      sparams->utils->free(query);
    }

    if (do_txn) {
      sparams->utils->log(NULL, SASL_LOG_DEBUG, "commit transaction");
      if (settings->sql_engine->sql_commit_txn(conn, sparams->utils)) {
          sparams->utils->log(NULL, SASL_LOG_ERR, 
                        "Unable to commit transaction\n");
      }
    }
    
  done:
    if (escap_userid) sparams->utils->free(escap_userid);
    if (escap_realm) sparams->utils->free(escap_realm);
    if (conn) settings->sql_engine->sql_close(conn);
    if (userid) sparams->utils->free(userid);
    if (realm) sparams->utils->free(realm);
    if (user_buf) sparams->utils->free(user_buf);
}

static int sql_auxprop_store(void *glob_context,
                       sasl_server_params_t *sparams,
                       struct propctx *ctx,
                       const char *user,
                       unsigned ulen) 
{
    char *userid = NULL;
    char *realm = NULL;
    const char *user_realm = NULL;
    int ret = SASL_FAIL;
    const struct propval *to_store, *cur;
    
    char *user_buf;
    char *statement = NULL;
    char *escap_userid = NULL;
    char *escap_realm = NULL;
    const char *cmd;
    
    sql_settings_t *settings;
    void *conn = NULL;
    
    settings = (sql_settings_t *) glob_context; 

    /* just checking if we are enabled */
    if (!ctx &&
      sql_exists(settings->sql_insert) &&
      sql_exists(settings->sql_update)) return SASL_OK;
    
    /* make sure our input is okay */
    if (!glob_context || !sparams || !user) return SASL_BADPARAM;
    
    sparams->utils->log(NULL, SASL_LOG_DEBUG,
                  "sql plugin Parse the username %s\n", user);
    
    user_buf = sparams->utils->malloc(ulen + 1);
    if (!user_buf) {
      ret = SASL_NOMEM;
      goto done;
    }
    
    memcpy(user_buf, user, ulen);
    user_buf[ulen] = '\0';
    
    if (sparams->user_realm) {
      user_realm = sparams->user_realm;
    }
    else {
      user_realm = sparams->serverFQDN;
    }
    
    ret = _plug_parseuser(sparams->utils, &userid, &realm, user_realm,
                    sparams->serverFQDN, user_buf);
    if (ret != SASL_OK) goto done;
    
    /* just need to escape userid and realm now */
    /* allocate some memory */
    
    escap_userid = (char *) sparams->utils->malloc(strlen(userid)*2+1);
    escap_realm = (char *) sparams->utils->malloc(strlen(realm)*2+1);
    
    if (!escap_userid || !escap_realm) {
      MEMERROR(sparams->utils);
      goto done;
    }
    
    to_store = sparams->utils->prop_get(ctx);
    
    if (!to_store) {
      ret = SASL_BADPARAM;
      goto done;
    }

    conn = sql_connect(settings, sparams->utils);
    if (!conn) {
      sparams->utils->log(NULL, SASL_LOG_ERR,
                      "sql plugin couldn't connect to any host\n");
      goto done;
    }
    
    settings->sql_engine->sql_escape_str(escap_userid, userid);
    settings->sql_engine->sql_escape_str(escap_realm, realm);
    
    if (settings->sql_engine->sql_begin_txn(conn, sparams->utils)) {
      sparams->utils->log(NULL, SASL_LOG_ERR, 
                      "Unable to begin transaction\n");
    }
    for (cur = to_store; ret == SASL_OK && cur->name; cur++) {
      /* determine which command we need */
      /* see if we already have a row for this user */
      statement = sql_create_statement(settings->sql_select,
                               SQL_WILDCARD, escap_userid,
                               escap_realm, NULL,
                               sparams->utils);
      if (!settings->sql_engine->sql_exec(conn, statement, NULL, 0, NULL,
                                  sparams->utils)) {
          /* already have a row => UPDATE */
          cmd = settings->sql_update;
      } else {
          /* new row => INSERT */
          cmd = settings->sql_insert;
      }
      sparams->utils->free(statement);

      /* create a statement that we will use */
      statement = sql_create_statement(cmd, cur->name, escap_userid,
                               escap_realm,
                               cur->values && cur->values[0] ?
                               cur->values[0] : SQL_NULL_VALUE,
                               sparams->utils);
      
      {
          char *log_statement =
            sql_create_statement(cmd, cur->name,
                             escap_userid,
                             escap_realm,
                             cur->values && cur->values[0] ?
                             "<omitted>" : SQL_NULL_VALUE,
                             sparams->utils);
          sparams->utils->log(NULL, SASL_LOG_DEBUG,
                        "sql plugin doing statement %s\n",
                        log_statement);
          sparams->utils->free(log_statement);
      }
      
      /* run the statement */
      if (settings->sql_engine->sql_exec(conn, statement, NULL, 0, NULL,
                                 sparams->utils)) {
          ret = SASL_FAIL;
      }
      
      sparams->utils->free(statement);
    }
    if (ret != SASL_OK) {
      sparams->utils->log(NULL, SASL_LOG_ERR,
                      "Failed to store auxprop; aborting transaction\n");
      if (settings->sql_engine->sql_rollback_txn(conn, sparams->utils)) {
          sparams->utils->log(NULL, SASL_LOG_ERR, 
                        "Unable to rollback transaction\n");
      }
    }
    else if (settings->sql_engine->sql_commit_txn(conn, sparams->utils)) {
      sparams->utils->log(NULL, SASL_LOG_ERR, 
                      "Unable to commit transaction\n");
    }
    
  done:
    if (escap_userid) sparams->utils->free(escap_userid);
    if (escap_realm) sparams->utils->free(escap_realm);
    if (conn) settings->sql_engine->sql_close(conn);
    if (userid) sparams->utils->free(userid);
    if (realm) sparams->utils->free(realm);
    if (user_buf) sparams->utils->free(user_buf);
    
    return ret;
    
    /* do a little dance */
}


static void sql_auxprop_free(void *glob_context, const sasl_utils_t *utils)
{
    sql_settings_t *settings;
    
    settings = (sql_settings_t *)glob_context;
    
    if (!settings) return;
    
    utils->log(NULL, SASL_LOG_DEBUG, "sql freeing memory\n");
    
    utils->free(settings);
}

static sasl_auxprop_plug_t sql_auxprop_plugin = {
    0,                  /* Features */
    0,                  /* spare */
    NULL,         /* glob_context */
    sql_auxprop_free,   /* auxprop_free */
    sql_auxprop_lookup, /* auxprop_lookup */
    "sql",        /* name */
    sql_auxprop_store   /* auxprop_store */
};

int sql_auxprop_plug_init(const sasl_utils_t *utils,
                    int max_version,
                    int *out_version,
                    sasl_auxprop_plug_t **plug,
                    const char *plugname __attribute__((unused))) 
{
    sql_settings_t *settings;
    
    if (!out_version || !plug) return SASL_BADPARAM;
    
    if (max_version < SASL_AUXPROP_PLUG_VERSION) return SASL_BADVERS;
    *out_version = SASL_AUXPROP_PLUG_VERSION;
    
    *plug = &sql_auxprop_plugin;
    
    settings = (sql_settings_t *) utils->malloc(sizeof(sql_settings_t));
    
    if (!settings) {
      MEMERROR(utils);
      return SASL_NOMEM;
    }
    
    memset(settings, 0, sizeof(sql_settings_t));
    sql_get_settings(utils, settings);
    
    if (!settings->sql_engine->name) return SASL_NOMECH;

    if (!sql_exists(settings->sql_select)) {
      utils->log(NULL, SASL_LOG_ERR, "sql_select option missing");
      utils->free(settings);  
      return SASL_NOMECH;
    }

    utils->log(NULL, SASL_LOG_DEBUG,
             "sql auxprop plugin using %s engine\n",
             settings->sql_engine->name);
    
    sql_auxprop_plugin.glob_context = settings;
    
    return SASL_OK;
}

Generated by  Doxygen 1.6.0   Back to index