1 /*
2 **
3 ** SQL Auxprop plugin
4 **
5 ** Ken Murchison
6 ** Maya Nigrosh -- original store() and txn support
7 ** Simon Loader -- original mysql plugin
8 ** Patrick Welche -- original pgsql plugin
9 **
10 **
11 */
12 
13 #include <config.h>
14 
15 #include <stdio.h>
16 #include <assert.h>
17 #include <stdlib.h>
18 #include <string.h>
19 
20 #include "sasl.h"
21 #include "saslutil.h"
22 #include "saslplug.h"
23 
24 #include <ctype.h>
25 
26 #include "plugin_common.h"
27 
28 #define sql_max(a, b) ((a) > (b) ? (a) : (b))
29 #define sql_len(input) ((input) ? strlen(input) : 0)
30 #define sql_exists(input) ((input) && (*input))
31 
32 typedef struct sql_engine {
33     const char *name;
34     void *(*sql_open)(char *host, char *port, int usessl,
35 		      const char *user, const char *password,
36 		      const char *database, const sasl_utils_t *utils);
37     int (*sql_escape_str)(char *to, const char *from);
38     int (*sql_begin_txn)(void *conn, const sasl_utils_t *utils);
39     int (*sql_commit_txn)(void *conn, const sasl_utils_t *utils);
40     int (*sql_rollback_txn)(void *conn, const sasl_utils_t *utils);
41     int (*sql_exec)(void *conn, const char *cmd, char *value, size_t size,
42 		    size_t *value_len, const sasl_utils_t *utils);
43     void (*sql_close)(void *conn);
44 } sql_engine_t;
45 
46 typedef struct sql_settings {
47     const sql_engine_t *sql_engine;
48     const char *sql_user;
49     const char *sql_passwd;
50     const char *sql_hostnames;
51     const char *sql_database;
52     const char *sql_select;
53     const char *sql_insert;
54     const char *sql_update;
55     int sql_usessl;
56 } sql_settings_t;
57 
58 static const char * SQL_BLANK_STRING = "";
59 static const char * SQL_WILDCARD = "*";
60 static const char * SQL_NULL_VALUE = "NULL";
61 
62 
63 #ifdef HAVE_MYSQL
64 #include <mysql.h>
65 
_mysql_open(char * host,char * port,int usessl,const char * user,const char * password,const char * database,const sasl_utils_t * utils)66 static void *_mysql_open(char *host, char *port, int usessl,
67 			 const char *user, const char *password,
68 			 const char *database, const sasl_utils_t *utils)
69 {
70     MYSQL *mysql;
71 
72     if (!(mysql = mysql_init(NULL))) {
73 	utils->log(utils->conn, SASL_LOG_ERR,
74 		   "sql plugin: could not execute mysql_init()");
75 	return NULL;
76     }
77 
78     return mysql_real_connect(mysql, host, user, password, database,
79 			      port ? strtoul(port, NULL, 10) : 0, NULL,
80 			      usessl ? CLIENT_SSL : 0);
81 }
82 
_mysql_escape_str(char * to,const char * from)83 static int _mysql_escape_str(char *to, const char *from)
84 {
85     return mysql_escape_string(to, from, strlen(from));
86 }
87 
_mysql_exec(void * conn,const char * cmd,char * value,size_t size,size_t * value_len,const sasl_utils_t * utils)88 static int _mysql_exec(void *conn, const char *cmd, char *value, size_t size,
89 		       size_t *value_len, const sasl_utils_t *utils)
90 {
91     MYSQL_RES *result;
92     MYSQL_ROW row;
93     int row_count, len;
94 
95     len = strlen(cmd);
96     /* mysql_real_query() doesn't want a terminating ';' */
97     if (cmd[len-1] == ';') len--;
98 
99     /*
100      *  Run the query. It is important to note that mysql_real_query
101      *  will return success even if the sql statement
102      *  had an error in it. However, mysql_errno() will alsways
103      *  tell us if there was an error. Therefore we can ignore
104      *  the result from mysql_real_query and simply check mysql_errno()
105      *  to decide if there was really an error.
106      */
107     (void)mysql_real_query(conn, cmd, len);
108 
109     if(mysql_errno(conn)) {
110         utils->log(utils->conn, SASL_LOG_ERR, "sql query failed: %s",
111 		   mysql_error(conn));
112 	return -1;
113     }
114 
115     /* see if we should expect some results */
116     if (!mysql_field_count(conn)) {
117 	/* no results (BEGIN, COMMIT, DELETE, INSERT, UPDATE) */
118 	return 0;
119     }
120 
121     /* get the results */
122     result = mysql_store_result(conn);
123     if (!result) {
124 	/* umm nothing found */
125 	utils->log(utils->conn, SASL_LOG_NOTE, "sql plugin: no result found");
126 	return -1;
127     }
128 
129     /* quick row check */
130     row_count = mysql_num_rows(result);
131     if (!row_count) {
132 	/* umm nothing found */
133 	mysql_free_result(result);
134 	utils->log(utils->conn, SASL_LOG_NOTE, "sql plugin: no result found");
135 	return -1;
136     }
137     if (row_count > 1) {
138 	utils->log(utils->conn, SASL_LOG_WARN,
139 		   "sql plugin: found duplicate row for query %s", cmd);
140     }
141 
142     /* now get the result set value and value_len */
143     /* we only fetch one because we don't care about the rest */
144     row = mysql_fetch_row(result);
145     if (!row || !row[0]) {
146 	/* umm nothing found */
147 	utils->log(utils->conn, SASL_LOG_NOTE, "sql plugin: no result found");
148 	mysql_free_result(result);
149 	return -1;
150     }
151     if (value) {
152 	strncpy(value, row[0], size-2);
153 	value[size-1] = '\0';
154 	if (value_len) *value_len = strlen(value);
155     }
156 
157     /* free result */
158     mysql_free_result(result);
159     mysql_next_result(conn);
160 
161     return 0;
162 }
163 
_mysql_begin_txn(void * conn,const sasl_utils_t * utils)164 static int _mysql_begin_txn(void *conn, const sasl_utils_t *utils)
165 {
166     return _mysql_exec(conn,
167 #if MYSQL_VERSION_ID >= 40011
168 		       "START TRANSACTION",
169 #else
170 		       "BEGIN",
171 #endif
172 		       NULL, 0, NULL, utils);
173 }
174 
_mysql_commit_txn(void * conn,const sasl_utils_t * utils)175 static int _mysql_commit_txn(void *conn, const sasl_utils_t *utils)
176 {
177     return _mysql_exec(conn, "COMMIT", NULL, 0, NULL, utils);
178 }
179 
_mysql_rollback_txn(void * conn,const sasl_utils_t * utils)180 static int _mysql_rollback_txn(void *conn, const sasl_utils_t *utils)
181 {
182     return _mysql_exec(conn, "ROLLBACK", NULL, 0, NULL, utils);
183 }
184 
_mysql_close(void * conn)185 static void _mysql_close(void *conn)
186 {
187     mysql_close(conn);
188 }
189 #endif /* HAVE_MYSQL */
190 
191 #ifdef HAVE_PGSQL
192 #include <libpq-fe.h>
193 
_pgsql_open(char * host,char * port,int usessl,const char * user,const char * password,const char * database,const sasl_utils_t * utils)194 static void *_pgsql_open(char *host, char *port, int usessl,
195 			 const char *user, const char *password,
196 			 const char *database, const sasl_utils_t *utils)
197 {
198     PGconn *conn = NULL;
199     char *conninfo, *sep;
200 
201     /* create the connection info string */
202     /* The 64 represents the number of characters taken by
203      * the keyword tokens, plus a small pad
204      */
205     conninfo = utils->malloc(64 + sql_len(host) + sql_len(port)
206 			     + sql_len(user) + sql_len(password)
207 			     + sql_len(database));
208     if (!conninfo) {
209 	MEMERROR(utils);
210 	return NULL;
211     }
212 
213     /* add each term that exists */
214     conninfo[0] = '\0';
215     sep = "";
216     if (sql_exists(host)) {
217 	strcat(conninfo, sep);
218 	strcat(conninfo, "host='");
219 	strcat(conninfo, host);
220 	strcat(conninfo, "'");
221 	sep = " ";
222     }
223     if (sql_exists(port)) {
224 	strcat(conninfo, sep);
225 	strcat(conninfo, "port='");
226 	strcat(conninfo, port);
227 	strcat(conninfo, "'");
228 	sep = " ";
229     }
230     if (sql_exists(user)) {
231 	strcat(conninfo, sep);
232 	strcat(conninfo, "user='");
233 	strcat(conninfo, user);
234 	strcat(conninfo, "'");
235 	sep = " ";
236     }
237     if (sql_exists(password)) {
238 	strcat(conninfo, sep);
239 	strcat(conninfo, "password='");
240 	strcat(conninfo, password);
241 	strcat(conninfo, "'");
242 	sep = " ";
243     }
244     if (sql_exists(database)) {
245 	strcat(conninfo, sep);
246 	strcat(conninfo, "dbname='");
247 	strcat(conninfo, database);
248 	strcat(conninfo, "'");
249 	sep = " ";
250     }
251     if (usessl) {
252 	strcat(conninfo, sep);
253 	strcat(conninfo, "requiressl='1'");
254     }
255 
256     conn = PQconnectdb(conninfo);
257     free(conninfo);
258 
259     if ((PQstatus(conn) != CONNECTION_OK)) {
260 	utils->log(utils->conn, SASL_LOG_ERR, "sql plugin: %s",
261 		   PQerrorMessage(conn));
262 	return NULL;
263     }
264 
265     return conn;
266 }
267 
_pgsql_escape_str(char * to,const char * from)268 static int _pgsql_escape_str(char *to, const char *from)
269 {
270     return PQescapeString(to, from, strlen(from));
271 }
272 
_pgsql_exec(void * conn,const char * cmd,char * value,size_t size,size_t * value_len,const sasl_utils_t * utils)273 static int _pgsql_exec(void *conn, const char *cmd, char *value, size_t size,
274 		       size_t *value_len, const sasl_utils_t *utils)
275 {
276     PGresult *result;
277     int row_count;
278     ExecStatusType status;
279 
280     /* run the query */
281     result = PQexec(conn, cmd);
282 
283     /* check the status */
284     status = PQresultStatus(result);
285     if (status == PGRES_COMMAND_OK) {
286 	/* no results (BEGIN, COMMIT, DELETE, INSERT, UPDATE) */
287 	PQclear(result);
288 	return 0;
289     }
290     else if (status != PGRES_TUPLES_OK) {
291 	/* error */
292 	utils->log(utils->conn, SASL_LOG_DEBUG, "sql plugin: %s ",
293 		   PQresStatus(status));
294 	PQclear(result);
295 	return -1;
296     }
297 
298     /* quick row check */
299     row_count = PQntuples(result);
300     if (!row_count) {
301 	/* umm nothing found */
302 	utils->log(utils->conn, SASL_LOG_NOTE, "sql plugin: no result found");
303 	PQclear(result);
304 	return -1;
305     }
306     if (row_count > 1) {
307 	utils->log(utils->conn, SASL_LOG_WARN,
308 		   "sql plugin: found duplicate row for query %s", cmd);
309     }
310 
311     /* now get the result set value and value_len */
312     /* we only fetch one because we don't care about the rest */
313     if (value) {
314 	strncpy(value, PQgetvalue(result,0,0), size-2);
315 	value[size-1] = '\0';
316 	if (value_len) *value_len = strlen(value);
317     }
318 
319     /* free result */
320     PQclear(result);
321     return 0;
322 }
323 
_pgsql_begin_txn(void * conn,const sasl_utils_t * utils)324 static int _pgsql_begin_txn(void *conn, const sasl_utils_t *utils)
325 {
326     return _pgsql_exec(conn, "BEGIN;", NULL, 0, NULL, utils);
327 }
328 
_pgsql_commit_txn(void * conn,const sasl_utils_t * utils)329 static int _pgsql_commit_txn(void *conn, const sasl_utils_t *utils)
330 {
331     return _pgsql_exec(conn, "COMMIT;", NULL, 0, NULL, utils);
332 }
333 
_pgsql_rollback_txn(void * conn,const sasl_utils_t * utils)334 static int _pgsql_rollback_txn(void *conn, const sasl_utils_t *utils)
335 {
336     return _pgsql_exec(conn, "ROLLBACK;", NULL, 0, NULL, utils);
337 }
338 
_pgsql_close(void * conn)339 static void _pgsql_close(void *conn)
340 {
341     PQfinish(conn);
342 }
343 #endif /* HAVE_PGSQL */
344 
345 #ifdef HAVE_SQLITE
346 #include <sqlite.h>
347 
_sqlite_open(char * host,char * port,int usessl,const char * user,const char * password,const char * database,const sasl_utils_t * utils)348 static void *_sqlite_open(char *host __attribute__((unused)),
349 			  char *port __attribute__((unused)),
350 			  int usessl __attribute__((unused)),
351 			  const char *user __attribute__((unused)),
352 			  const char *password __attribute__((unused)),
353 			  const char *database, const sasl_utils_t *utils)
354 {
355     int rc;
356     sqlite *db;
357     char *zErrMsg = NULL;
358 
359     db = sqlite_open(database, 0, &zErrMsg);
360     if (db == NULL) {
361 	utils->log(utils->conn, SASL_LOG_ERR, "sql plugin: %s", zErrMsg);
362 	sqlite_freemem (zErrMsg);
363 	return NULL;
364     }
365 
366     rc = sqlite_exec(db, "PRAGMA empty_result_callbacks = ON", NULL, NULL, &zErrMsg);
367     if (rc != SQLITE_OK) {
368 	utils->log(utils->conn, SASL_LOG_ERR, "sql plugin: %s", zErrMsg);
369 	sqlite_freemem (zErrMsg);
370 	sqlite_close(db);
371 	return NULL;
372     }
373 
374     return (void*)db;
375 }
376 
_sqlite_escape_str(char * to,const char * from)377 static int _sqlite_escape_str(char *to, const char *from)
378 {
379     char s;
380 
381     while ( (s = *from++) != '\0' ) {
382 	if (s == '\'' || s == '\\') {
383 	    *to++ = '\\';
384 	}
385 	*to++ = s;
386     }
387     *to = '\0';
388 
389     return 0;
390 }
391 
sqlite_my_callback(void * pArg,int argc,char ** argv,char ** columnNames)392 static int sqlite_my_callback(void *pArg, int argc __attribute__((unused)),
393 			      char **argv,
394 			      char **columnNames __attribute__((unused)))
395 {
396     char **result = (char**)pArg;
397 
398     if (argv == NULL) {
399 	*result = NULL;				/* no record */
400     } else if (argv[0] == NULL) {
401 	*result = strdup(SQL_NULL_VALUE);	/* NULL IS SQL_NULL_VALUE */
402     } else {
403 	*result = strdup(argv[0]);
404     }
405 
406     return /*ABORT*/1;
407 }
408 
_sqlite_exec(void * db,const char * cmd,char * value,size_t size,size_t * value_len,const sasl_utils_t * utils)409 static int _sqlite_exec(void *db, const char *cmd, char *value, size_t size,
410 		        size_t *value_len, const sasl_utils_t *utils)
411 {
412     int rc;
413     char *result = NULL;
414     char *zErrMsg = NULL;
415 
416     rc = sqlite_exec((sqlite*)db, cmd, sqlite_my_callback, (void*)&result, &zErrMsg);
417     if (rc != SQLITE_OK && rc != SQLITE_ABORT) {
418 	utils->log(utils->conn, SASL_LOG_DEBUG, "sql plugin: %s ", zErrMsg);
419 	sqlite_freemem (zErrMsg);
420 	return -1;
421     }
422 
423     if (rc == SQLITE_OK) {
424 	/* no results (BEGIN, COMMIT, DELETE, INSERT, UPDATE) */
425 	return 0;
426     }
427 
428     if (result == NULL) {
429 	/* umm nothing found */
430 	utils->log(utils->conn, SASL_LOG_NOTE, "sql plugin: no result found");
431 	return -1;
432     }
433 
434     /* XXX: Duplication cannot be found by this method. */
435 
436     /* now get the result set value and value_len */
437     /* we only fetch one because we don't care about the rest */
438     if (value) {
439 	strncpy(value, result, size - 2);
440 	value[size - 1] = '\0';
441 	if (value_len) {
442 	    *value_len = strlen(value);
443 	}
444     }
445 
446     /* free result */
447     free(result);
448     return 0;
449 }
450 
_sqlite_begin_txn(void * db,const sasl_utils_t * utils)451 static int _sqlite_begin_txn(void *db, const sasl_utils_t *utils)
452 {
453     return _sqlite_exec(db, "BEGIN TRANSACTION", NULL, 0, NULL, utils);
454 }
455 
_sqlite_commit_txn(void * db,const sasl_utils_t * utils)456 static int _sqlite_commit_txn(void *db, const sasl_utils_t *utils)
457 {
458     return _sqlite_exec(db, "COMMIT TRANSACTION", NULL, 0, NULL, utils);
459 }
460 
_sqlite_rollback_txn(void * db,const sasl_utils_t * utils)461 static int _sqlite_rollback_txn(void *db, const sasl_utils_t *utils)
462 {
463     return _sqlite_exec(db, "ROLLBACK TRANSACTION", NULL, 0, NULL, utils);
464 }
465 
_sqlite_close(void * db)466 static void _sqlite_close(void *db)
467 {
468     sqlite_close((sqlite*)db);
469 }
470 #endif /* HAVE_SQLITE */
471 
472 #ifdef HAVE_SQLITE3
473 #include <sqlite3.h>
474 
_sqlite3_open(char * host,char * port,int usessl,const char * user,const char * password,const char * database,const sasl_utils_t * utils)475 static void *_sqlite3_open(char *host __attribute__((unused)),
476 			  char *port __attribute__((unused)),
477 			  int usessl __attribute__((unused)),
478 			  const char *user __attribute__((unused)),
479 			  const char *password __attribute__((unused)),
480 			  const char *database, const sasl_utils_t *utils)
481 {
482     int rc;
483     sqlite3 *db;
484     char *zErrMsg = NULL;
485 
486     rc = sqlite3_open(database, &db);
487     if (SQLITE_OK != rc) {
488     	if (db)
489 		utils->log(utils->conn, SASL_LOG_ERR, "sql plugin: %s",
490 			   sqlite3_errmsg(db));
491 	else
492 		utils->log(utils->conn, SASL_LOG_ERR, "sql plugin: %d", rc);
493 	sqlite3_close(db);
494 	return NULL;
495     }
496 
497     rc = sqlite3_exec(db, "PRAGMA empty_result_callbacks = ON", NULL, NULL, &zErrMsg);
498     if (rc != SQLITE_OK) {
499     	if (zErrMsg) {
500 		utils->log(utils->conn, SASL_LOG_ERR, "sql plugin: %s",
501 			   zErrMsg);
502 		sqlite3_free(zErrMsg);
503 	} else
504 		utils->log(utils->conn, SASL_LOG_DEBUG, "sql plugin: %d", rc);
505 	sqlite3_close(db);
506 	return NULL;
507     }
508 
509     return (void*)db;
510 }
511 
_sqlite3_escape_str(char * to,const char * from)512 static int _sqlite3_escape_str(char *to, const char *from)
513 {
514     char s;
515 
516     while ( (s = *from++) != '\0' ) {
517 	if (s == '\'' || s == '\\') {
518 	    *to++ = '\\';
519 	}
520 	*to++ = s;
521     }
522     *to = '\0';
523 
524     return 0;
525 }
526 
sqlite3_my_callback(void * pArg,int argc,char ** argv,char ** columnNames)527 static int sqlite3_my_callback(void *pArg, int argc __attribute__((unused)),
528 			      char **argv,
529 			      char **columnNames __attribute__((unused)))
530 {
531     char **result = (char**)pArg;
532 
533     if (argv == NULL) {
534 	*result = NULL;				/* no record */
535     } else if (argv[0] == NULL) {
536 	*result = strdup(SQL_NULL_VALUE);	/* NULL IS SQL_NULL_VALUE */
537     } else {
538 	*result = strdup(argv[0]);
539     }
540 
541     return 0;
542 }
543 
_sqlite3_exec(void * db,const char * cmd,char * value,size_t size,size_t * value_len,const sasl_utils_t * utils)544 static int _sqlite3_exec(void *db,
545 			 const char *cmd,
546 			 char *value,
547 			 size_t size,
548 			 size_t *value_len,
549 			 const sasl_utils_t *utils)
550 {
551     int rc;
552     char *result = NULL;
553     char *zErrMsg = NULL;
554 
555     rc = sqlite3_exec((sqlite3*)db, cmd, sqlite3_my_callback, (void*)&result, &zErrMsg);
556     if (rc != SQLITE_OK) {
557     	if (zErrMsg) {
558 	    utils->log(utils->conn, SASL_LOG_DEBUG, "sql plugin: %s", zErrMsg);
559 	    sqlite3_free(zErrMsg);
560 	} else {
561 	    utils->log(utils->conn, SASL_LOG_DEBUG, "sql plugin: %d", rc);
562 	}
563 	return -1;
564     }
565 
566     if (value == NULL && rc == SQLITE_OK) {
567 	/* no results (BEGIN, COMMIT, DELETE, INSERT, UPDATE) */
568 	return 0;
569     }
570 
571     if (result == NULL) {
572 	/* umm nothing found */
573 	utils->log(utils->conn, SASL_LOG_NOTE, "sql plugin: no result found");
574 	return -1;
575     }
576 
577     /* XXX: Duplication cannot be found by this method. */
578 
579     /* now get the result set value and value_len */
580     /* we only fetch one because we don't care about the rest */
581     if (value) {
582 	strncpy(value, result, size - 2);
583 	value[size - 1] = '\0';
584 	if (value_len) {
585 	    *value_len = strlen(value);
586 	}
587     }
588 
589     free(result);
590     return 0;
591 }
592 
_sqlite3_begin_txn(void * db,const sasl_utils_t * utils)593 static int _sqlite3_begin_txn(void *db, const sasl_utils_t *utils)
594 {
595     return _sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, 0, NULL, utils);
596 }
597 
_sqlite3_commit_txn(void * db,const sasl_utils_t * utils)598 static int _sqlite3_commit_txn(void *db, const sasl_utils_t *utils)
599 {
600     return _sqlite3_exec(db, "COMMIT TRANSACTION;", NULL, 0, NULL, utils);
601 }
602 
_sqlite3_rollback_txn(void * db,const sasl_utils_t * utils)603 static int _sqlite3_rollback_txn(void *db, const sasl_utils_t *utils)
604 {
605     return _sqlite3_exec(db, "ROLLBACK TRANSACTION;", NULL, 0, NULL, utils);
606 }
607 
_sqlite3_close(void * db)608 static void _sqlite3_close(void *db)
609 {
610     sqlite3_close((sqlite3*)db);
611 }
612 #endif /* HAVE_SQLITE3 */
613 
614 static const sql_engine_t sql_engines[] = {
615 #ifdef HAVE_MYSQL
616     { "mysql", &_mysql_open, &_mysql_escape_str,
617       &_mysql_begin_txn, &_mysql_commit_txn, &_mysql_rollback_txn,
618       &_mysql_exec, &_mysql_close },
619 #endif /* HAVE_MYSQL */
620 #ifdef HAVE_PGSQL
621     { "pgsql", &_pgsql_open, &_pgsql_escape_str,
622       &_pgsql_begin_txn, &_pgsql_commit_txn, &_pgsql_rollback_txn,
623       &_pgsql_exec, &_pgsql_close },
624 #endif
625 #ifdef HAVE_SQLITE
626     { "sqlite", &_sqlite_open, &_sqlite_escape_str,
627       &_sqlite_begin_txn, &_sqlite_commit_txn, &_sqlite_rollback_txn,
628       &_sqlite_exec, &_sqlite_close },
629 #endif
630 #ifdef HAVE_SQLITE3
631     { "sqlite3", &_sqlite3_open, &_sqlite3_escape_str,
632       &_sqlite3_begin_txn, &_sqlite3_commit_txn, &_sqlite3_rollback_txn,
633       &_sqlite3_exec, &_sqlite3_close },
634 #endif
635     { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
636 };
637 
638 /*
639 **  Sql_create_statement
640 **   uses statement line and allocate memory to replace
641 **  Parts with the strings provided.
642 **   %<char> =  no change
643 **   %% = %
644 **   %u = user
645 **   %p = prop
646 **   %r = realm
647 **   %v = value of prop
648 **  e.g select %p from auth where user = %u and domain = %r;
649 **  Note: calling function must free memory.
650 **
651 */
652 
sql_create_statement(const char * statement,const char * prop,const char * user,const char * realm,const char * value,const sasl_utils_t * utils)653 static char *sql_create_statement(const char *statement, const char *prop,
654 				  const char *user, const char *realm,
655 				  const char *value,
656 				  const sasl_utils_t *utils)
657 {
658     const char *ptr, *line_ptr;
659     char *buf, *buf_ptr;
660     int filtersize;
661     int ulen, plen, rlen, vlen;
662     int numpercents=0;
663     int biggest;
664     size_t i;
665 
666     /* calculate memory needed for creating the complete query string. */
667     ulen = (int)strlen(user);
668     rlen = (int)strlen(realm);
669     plen = (int)strlen(prop);
670     vlen = (int)sql_len(value);
671 
672     /* what if we have multiple %foo occurrences in the input query? */
673     for (i = 0; i < strlen(statement); i++) {
674 	if (statement[i] == '%') {
675 	    numpercents++;
676 	}
677     }
678 
679     /* find the biggest of ulen, rlen, plen, vlen */
680     biggest = sql_max(sql_max(ulen, rlen), sql_max(plen, vlen));
681 
682     /* plus one for the semicolon...and don't forget the trailing 0x0 */
683     filtersize = (int)strlen(statement) + 1 + (numpercents*biggest)+1;
684 
685     /* ok, now try to allocate a chunk of that size */
686     buf = (char *) utils->malloc(filtersize);
687 
688     if (!buf) {
689 	MEMERROR(utils);
690 	return NULL;
691     }
692 
693     buf_ptr = buf;
694     line_ptr = statement;
695 
696     /* replace the strings */
697     while ( (ptr = strchr(line_ptr, '%')) ) {
698 	/* copy up to but not including the next % */
699 	memcpy(buf_ptr, line_ptr, ptr - line_ptr);
700 	buf_ptr += ptr - line_ptr;
701 	ptr++;
702 	switch (ptr[0]) {
703 	case '%':
704 	    buf_ptr[0] = '%';
705 	    buf_ptr++;
706 	    break;
707 	case 'u':
708 	    memcpy(buf_ptr, user, ulen);
709 	    buf_ptr += ulen;
710 	    break;
711 	case 'r':
712 	    memcpy(buf_ptr, realm, rlen);
713 	    buf_ptr += rlen;
714 	    break;
715 	case 'p':
716 	    memcpy(buf_ptr, prop, plen);
717 	    buf_ptr += plen;
718 	    break;
719 	case 'v':
720 	    if (value != NULL) {
721 		memcpy(buf_ptr, value, vlen);
722 		buf_ptr += vlen;
723 	    }
724 	    else {
725 		utils->log(utils->conn, SASL_LOG_ERR,
726 			   "'%%v' shouldn't be in a SELECT or DELETE");
727 	    }
728 	    break;
729 	default:
730 	    buf_ptr[0] = '%';
731 	    buf_ptr[1] = ptr[0];
732 	    buf_ptr += 2;
733 	    break;
734 	}
735 	ptr++;
736 	line_ptr = ptr;
737     }
738 
739     memcpy(buf_ptr, line_ptr, strlen(line_ptr)+1);
740     /* Make sure the current portion of the statement ends with a semicolon */
741     if (buf_ptr[strlen(buf_ptr-1)] != ';') {
742 	strcat(buf_ptr, ";");
743     }
744 
745     return (buf);
746 }
747 
748 /* sql_get_settings
749  *
750  * Get the auxprop settings and put them in the global context array
751 */
sql_get_settings(const sasl_utils_t * utils,void * glob_context)752 static void sql_get_settings(const sasl_utils_t *utils, void *glob_context)
753 {
754     sql_settings_t *settings;
755     int r;
756     const char *usessl, *engine_name;
757     const sql_engine_t *e;
758 
759     settings = (sql_settings_t *) glob_context;
760 
761     r = utils->getopt(utils->getopt_context,"SQL", "sql_engine",
762 		      &engine_name, NULL);
763     if (r || !engine_name) {
764 	engine_name = "mysql";
765     }
766 
767     /* find the correct engine */
768     e = sql_engines;
769     while (e->name) {
770 	if (!strcasecmp(engine_name, e->name)) break;
771 	e++;
772     }
773 
774     if (!e->name) {
775 	utils->log(utils->conn, SASL_LOG_ERR, "SQL engine '%s' not supported",
776 		   engine_name);
777     }
778 
779     settings->sql_engine = e;
780 
781     r = utils->getopt(utils->getopt_context,"SQL","sql_user",
782 		      &settings->sql_user, NULL);
783     if ( r || !settings->sql_user ) {
784 	settings->sql_user = SQL_BLANK_STRING;
785     }
786 
787     r = utils->getopt(utils->getopt_context,"SQL", "sql_passwd",
788 		      &settings->sql_passwd, NULL);
789     if (r || !settings->sql_passwd ) {
790 	settings->sql_passwd = SQL_BLANK_STRING;
791     }
792 
793     r = utils->getopt(utils->getopt_context,"SQL", "sql_hostnames",
794 		      &settings->sql_hostnames, NULL);
795     if (r || !settings->sql_hostnames ) {
796 	settings->sql_hostnames = SQL_BLANK_STRING;
797     }
798 
799     r = utils->getopt(utils->getopt_context,"SQL", "sql_database",
800 		      &settings->sql_database, NULL);
801     if (r || !settings->sql_database ) {
802 	settings->sql_database = SQL_BLANK_STRING;
803     }
804 
805     r = utils->getopt(utils->getopt_context,"SQL", "sql_select",
806 		      &settings->sql_select, NULL);
807     if (r || !settings->sql_select ) {
808 	/* backwards compatibility */
809 	r = utils->getopt(utils->getopt_context,"SQL", "sql_statement",
810 			  &settings->sql_select, NULL);
811 	if (r || !settings->sql_select) {
812 	    settings->sql_select = SQL_BLANK_STRING;
813 	}
814     }
815 
816     r = utils->getopt(utils->getopt_context, "SQL", "sql_insert",
817 		      &settings->sql_insert, NULL);
818     if (r || !settings->sql_insert) {
819 	settings->sql_insert = SQL_BLANK_STRING;
820     }
821 
822     r = utils->getopt(utils->getopt_context, "SQL", "sql_update",
823 		      &settings->sql_update, NULL);
824     if (r || !settings->sql_update) {
825 	settings->sql_update = SQL_BLANK_STRING;
826     }
827 
828     r = utils->getopt(utils->getopt_context, "SQL", "sql_usessl",
829 		  &usessl, NULL);
830     if (r || !usessl) usessl = "no";
831 
832     if (*usessl == '1' || *usessl == 'y'  || *usessl == 't' ||
833 	(*usessl == 'o' && usessl[1] == 'n')) {
834 	settings->sql_usessl = 1;
835     } else {
836 	settings->sql_usessl = 0;
837     }
838 }
839 
sql_connect(sql_settings_t * settings,const sasl_utils_t * utils)840 static void *sql_connect(sql_settings_t *settings, const sasl_utils_t *utils)
841 {
842     void *conn = NULL;
843     char *db_host_ptr = NULL;
844     char *db_host = NULL;
845     char *cur_host, *cur_port;
846 
847     /* loop around hostnames till we get a connection
848      * it should probably save the connection but for
849      * now we will just disconnect everytime
850      */
851     utils->log(utils->conn, SASL_LOG_DEBUG,
852 	       "sql plugin try and connect to a host\n");
853 
854     /* create a working version of the hostnames */
855     _plug_strdup(utils, settings->sql_hostnames, &db_host_ptr, NULL);
856     db_host = db_host_ptr;
857     cur_host = db_host;
858     while (cur_host != NULL) {
859 	db_host = strchr(db_host,',');
860 	if (db_host != NULL) {
861 	    db_host[0] = '\0';
862 
863 	    /* loop till we find some text */
864 	    while (!isalnum(db_host[0])) db_host++;
865 	}
866 
867 	utils->log(utils->conn, SASL_LOG_DEBUG,
868 		   "sql plugin trying to open db '%s' on host '%s'%s\n",
869 		   settings->sql_database, cur_host,
870 		   settings->sql_usessl ? " using SSL" : "");
871 
872 	/* set the optional port */
873 	if ((cur_port = strchr(cur_host, ':'))) *cur_port++ = '\0';
874 
875 	conn = settings->sql_engine->sql_open(cur_host, cur_port,
876 					      settings->sql_usessl,
877 					      settings->sql_user,
878 					      settings->sql_passwd,
879 					      settings->sql_database,
880 					      utils);
881 	if (conn) break;
882 
883 	utils->log(utils->conn, SASL_LOG_ERR,
884 		   "sql plugin could not connect to host %s", cur_host);
885 
886 	cur_host = db_host;
887     }
888 
889     if (db_host_ptr) utils->free(db_host_ptr);
890 
891     return conn;
892 }
893 
sql_auxprop_lookup(void * glob_context,sasl_server_params_t * sparams,unsigned flags,const char * user,unsigned ulen)894 static int sql_auxprop_lookup(void *glob_context,
895 			       sasl_server_params_t *sparams,
896 			       unsigned flags,
897 			       const char *user,
898 			       unsigned ulen)
899 {
900     char *userid = NULL;
901     /* realm could be used for something clever */
902     char *realm = NULL;
903     const char *user_realm = NULL;
904     const struct propval *to_fetch, *cur;
905     char value[8192];
906     size_t value_len;
907     char *user_buf;
908     char *query = NULL;
909     char *escap_userid = NULL;
910     char *escap_realm = NULL;
911     sql_settings_t *settings;
912     int verify_against_hashed_password;
913     int saw_user_password = 0;
914     void *conn = NULL;
915     int do_txn = 0;
916     int ret;
917 
918     if (!glob_context || !sparams || !user) return SASL_BADPARAM;
919 
920     /* setup the settings */
921     settings = (sql_settings_t *) glob_context;
922 
923     sparams->utils->log(sparams->utils->conn, SASL_LOG_DEBUG,
924 			"sql plugin Parse the username %s\n", user);
925 
926     user_buf = sparams->utils->malloc(ulen + 1);
927     if (!user_buf) {
928 	ret = SASL_NOMEM;
929 	goto done;
930     }
931 
932     memcpy(user_buf, user, ulen);
933     user_buf[ulen] = '\0';
934 
935     if(sparams->user_realm) {
936 	user_realm = sparams->user_realm;
937     } else {
938 	user_realm = sparams->serverFQDN;
939     }
940 
941     if ((ret = _plug_parseuser(sparams->utils,
942 			       &userid,
943 			       &realm,
944 			       user_realm,
945 			       sparams->serverFQDN,
946 			       user_buf)) != SASL_OK ) {
947 	goto done;
948     }
949 
950     /* just need to escape userid and realm now */
951     /* allocate some memory */
952     escap_userid = (char *)sparams->utils->malloc(strlen(userid)*2+1);
953     escap_realm = (char *)sparams->utils->malloc(strlen(realm)*2+1);
954 
955     if (!escap_userid || !escap_realm) {
956 	ret = SASL_NOMEM;
957 	goto done;
958     }
959 
960     /*************************************/
961 
962     /* find out what we need to get */
963     /* this corrupts const char *user */
964     to_fetch = sparams->utils->prop_get(sparams->propctx);
965     if (!to_fetch) {
966 	ret = SASL_NOMEM;
967 	goto done;
968     }
969 
970     conn = sql_connect(settings, sparams->utils);
971     if (!conn) {
972 	sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR,
973 			    "sql plugin couldn't connect to any host\n");
974 	/* TODO: in the future we might want to extend the internal
975 	   SQL driver API to return a more detailed error */
976 	ret = SASL_FAIL;
977 	goto done;
978     }
979 
980     /* escape out */
981     settings->sql_engine->sql_escape_str(escap_userid, userid);
982     settings->sql_engine->sql_escape_str(escap_realm, realm);
983 
984     verify_against_hashed_password = flags & SASL_AUXPROP_VERIFY_AGAINST_HASH;
985 
986     /* Assume that nothing is found */
987     ret = SASL_NOUSER;
988     for (cur = to_fetch; cur->name; cur++) {
989 	char *realname = (char *) cur->name;
990 
991 	/* Only look up properties that apply to this lookup! */
992 	if (cur->name[0] == '*'
993 	    && (flags & SASL_AUXPROP_AUTHZID))
994 	    continue;
995 	if (!(flags & SASL_AUXPROP_AUTHZID)) {
996 	    if(cur->name[0] != '*')
997 		continue;
998 	    else
999 		realname = (char*)cur->name + 1;
1000 	}
1001 
1002 	/* If it's there already, we want to see if it needs to be
1003 	 * overridden. userPassword is a special case, because it's value
1004 	   is always present if SASL_AUXPROP_VERIFY_AGAINST_HASH is specified.
1005 	   When SASL_AUXPROP_VERIFY_AGAINST_HASH is set, we just clear userPassword. */
1006 	if (cur->values && !(flags & SASL_AUXPROP_OVERRIDE) &&
1007 	    (verify_against_hashed_password == 0 ||
1008 	     strcasecmp(realname, SASL_AUX_PASSWORD_PROP) != 0)) {
1009 	    continue;
1010 	} else if (cur->values) {
1011 	    sparams->utils->prop_erase(sparams->propctx, cur->name);
1012 	}
1013 
1014 	if (strcasecmp(realname, SASL_AUX_PASSWORD_PROP) == 0) {
1015 	    saw_user_password = 1;
1016 	}
1017 
1018 	if (!do_txn) {
1019 	    do_txn = 1;
1020 	    sparams->utils->log(sparams->utils->conn, SASL_LOG_DEBUG,
1021 				"begin transaction");
1022 	    if (settings->sql_engine->sql_begin_txn(conn, sparams->utils)) {
1023 		sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR,
1024 				    "Unable to begin transaction\n");
1025 	    }
1026 	}
1027 
1028 	sparams->utils->log(sparams->utils->conn, SASL_LOG_DEBUG,
1029 			    "sql plugin create statement from %s %s %s\n",
1030 			    realname, escap_userid, escap_realm);
1031 
1032 	/* create a statement that we will use */
1033 	query = sql_create_statement(settings->sql_select,
1034 				     realname,escap_userid,
1035 				     escap_realm, NULL,
1036 				     sparams->utils);
1037 	if (query == NULL) {
1038 	    ret = SASL_NOMEM;
1039 	    break;
1040 	}
1041 
1042 	sparams->utils->log(sparams->utils->conn, SASL_LOG_DEBUG,
1043 			    "sql plugin doing query %s\n", query);
1044 
1045 	value[0] = '\0';
1046 	value_len = 0;
1047 	/* run the query */
1048 	if (!settings->sql_engine->sql_exec(conn, query, value, sizeof(value),
1049 					    &value_len, sparams->utils)) {
1050 	    sparams->utils->prop_set(sparams->propctx,
1051 				     cur->name,
1052 				     value,
1053 				     (int)value_len);
1054 	    ret = SASL_OK;
1055 	}
1056 
1057 	sparams->utils->free(query);
1058     }
1059 
1060     if (flags & SASL_AUXPROP_AUTHZID) {
1061 	/* This is a lie, but the caller can't handle
1062 	   when we return SASL_NOUSER for authorization identity lookup. */
1063 	if (ret == SASL_NOUSER) {
1064 	    ret = SASL_OK;
1065 	}
1066     } else {
1067 	if (ret == SASL_NOUSER && saw_user_password == 0) {
1068 	    /* Verify user existence by checking presence of
1069 	       the userPassword attribute */
1070 	    if (!do_txn) {
1071 		do_txn = 1;
1072 		sparams->utils->log(sparams->utils->conn, SASL_LOG_DEBUG,
1073 				    "begin transaction");
1074 		if (settings->sql_engine->sql_begin_txn(conn, sparams->utils)) {
1075 		    sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR,
1076 					"Unable to begin transaction\n");
1077 		}
1078 	    }
1079 
1080 	    sparams->utils->log(sparams->utils->conn, SASL_LOG_DEBUG,
1081 				"sql plugin create statement from %s %s %s\n",
1082 				SASL_AUX_PASSWORD_PROP,
1083 				escap_userid,
1084 				escap_realm);
1085 
1086 	    /* create a statement that we will use */
1087 	    query = sql_create_statement(settings->sql_select,
1088 					 SASL_AUX_PASSWORD_PROP,
1089 					 escap_userid,
1090 					 escap_realm,
1091 					 NULL,
1092 					 sparams->utils);
1093 	    if (query == NULL) {
1094 		ret = SASL_NOMEM;
1095 	    } else {
1096 		sparams->utils->log(sparams->utils->conn, SASL_LOG_DEBUG,
1097 				    "sql plugin doing query %s\n", query);
1098 
1099 		value[0] = '\0';
1100 		value_len = 0;
1101 		/* run the query */
1102 		if (!settings->sql_engine->sql_exec(conn,
1103 						    query,
1104 						    value,
1105 						    sizeof(value),
1106 						    &value_len,
1107 						    sparams->utils)) {
1108 		    ret = SASL_OK;
1109 		}
1110 
1111 		sparams->utils->free(query);
1112 	    }
1113 	}
1114     }
1115 
1116 
1117     if (do_txn) {
1118 	sparams->utils->log(sparams->utils->conn, SASL_LOG_DEBUG,
1119 			    "commit transaction");
1120 	if (settings->sql_engine->sql_commit_txn(conn, sparams->utils)) {
1121 	    sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR,
1122 				"Unable to commit transaction\n");
1123 	    /* Failure of the commit is non fatal when reading values */
1124 	}
1125     }
1126 
1127   done:
1128     if (escap_userid) sparams->utils->free(escap_userid);
1129     if (escap_realm) sparams->utils->free(escap_realm);
1130     if (conn) settings->sql_engine->sql_close(conn);
1131     if (userid) sparams->utils->free(userid);
1132     if (realm) sparams->utils->free(realm);
1133     if (user_buf) sparams->utils->free(user_buf);
1134 
1135     return (ret);
1136 }
1137 
sql_auxprop_store(void * glob_context,sasl_server_params_t * sparams,struct propctx * ctx,const char * user,unsigned ulen)1138 static int sql_auxprop_store(void *glob_context,
1139 			     sasl_server_params_t *sparams,
1140 			     struct propctx *ctx,
1141 			     const char *user,
1142 			     unsigned ulen)
1143 {
1144     char *userid = NULL;
1145     char *realm = NULL;
1146     const char *user_realm = NULL;
1147     int ret = SASL_FAIL;
1148     const struct propval *to_store, *cur;
1149 
1150     char *user_buf;
1151     char *statement = NULL;
1152     char *escap_userid = NULL;
1153     char *escap_realm = NULL;
1154     char *escap_passwd = NULL;
1155     const char *cmd;
1156 
1157     sql_settings_t *settings;
1158     void *conn = NULL;
1159 
1160     settings = (sql_settings_t *) glob_context;
1161 
1162     /* just checking if we are enabled */
1163     if (!ctx &&
1164 	sql_exists(settings->sql_insert) &&
1165 	sql_exists(settings->sql_update)) return SASL_OK;
1166 
1167     /* make sure our input is okay */
1168     if (!glob_context || !sparams || !user) return SASL_BADPARAM;
1169 
1170     sparams->utils->log(sparams->utils->conn, SASL_LOG_DEBUG,
1171 			"sql plugin Parse the username %s\n", user);
1172 
1173     user_buf = sparams->utils->malloc(ulen + 1);
1174     if (!user_buf) {
1175 	ret = SASL_NOMEM;
1176 	goto done;
1177     }
1178 
1179     memcpy(user_buf, user, ulen);
1180     user_buf[ulen] = '\0';
1181 
1182     if (sparams->user_realm) {
1183 	user_realm = sparams->user_realm;
1184     }
1185     else {
1186 	user_realm = sparams->serverFQDN;
1187     }
1188 
1189     ret = _plug_parseuser(sparams->utils, &userid, &realm, user_realm,
1190 			  sparams->serverFQDN, user_buf);
1191     if (ret != SASL_OK)	goto done;
1192 
1193     /* just need to escape userid and realm now */
1194     /* allocate some memory */
1195 
1196     escap_userid = (char *) sparams->utils->malloc(strlen(userid)*2+1);
1197     escap_realm = (char *) sparams->utils->malloc(strlen(realm)*2+1);
1198 
1199     if (!escap_userid || !escap_realm) {
1200 	MEMERROR(sparams->utils);
1201 	goto done;
1202     }
1203 
1204     to_store = sparams->utils->prop_get(ctx);
1205 
1206     if (!to_store) {
1207 	ret = SASL_BADPARAM;
1208 	goto done;
1209     }
1210 
1211     conn = sql_connect(settings, sparams->utils);
1212     if (!conn) {
1213 	sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR,
1214 			    "sql plugin couldn't connect to any host\n");
1215 	goto done;
1216     }
1217 
1218     settings->sql_engine->sql_escape_str(escap_userid, userid);
1219     settings->sql_engine->sql_escape_str(escap_realm, realm);
1220 
1221     if (settings->sql_engine->sql_begin_txn(conn, sparams->utils)) {
1222 	sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR,
1223 			    "Unable to begin transaction\n");
1224     }
1225     for (cur = to_store; ret == SASL_OK && cur->name; cur++) {
1226 	/* Free the buffer, current content is from previous loop. */
1227 	if (escap_passwd) {
1228 	    sparams->utils->free(escap_passwd);
1229 	    escap_passwd = NULL;
1230 	}
1231 
1232 	if (cur->name[0] == '*') {
1233 	    continue;
1234 	}
1235 
1236 	/* determine which command we need */
1237 	/* see if we already have a row for this user */
1238 	statement = sql_create_statement(settings->sql_select,
1239 					 SQL_WILDCARD, escap_userid,
1240 					 escap_realm, NULL,
1241 					 sparams->utils);
1242 	if (!settings->sql_engine->sql_exec(conn, statement, NULL, 0, NULL,
1243 					    sparams->utils)) {
1244 	    /* already have a row => UPDATE */
1245 	    cmd = settings->sql_update;
1246 	} else {
1247 	    /* new row => INSERT */
1248 	    cmd = settings->sql_insert;
1249 	}
1250 	sparams->utils->free(statement);
1251 
1252 	if (cur->values[0]) {
1253 	    escap_passwd = (char *)sparams->utils->malloc(strlen(cur->values[0])*2+1);
1254 	    if (!escap_passwd) {
1255 		ret = SASL_NOMEM;
1256 		break;
1257 	    }
1258 	    settings->sql_engine->sql_escape_str(escap_passwd, cur->values[0]);
1259 	}
1260 
1261 	/* create a statement that we will use */
1262 	statement = sql_create_statement(cmd, cur->name, escap_userid,
1263 					 escap_realm,
1264 					 escap_passwd ?
1265 					 escap_passwd : SQL_NULL_VALUE,
1266 					 sparams->utils);
1267 	if (!statement) {
1268 	    ret = SASL_NOMEM;
1269 	    break;
1270 	}
1271 
1272 	{
1273 	    char *log_statement =
1274 		sql_create_statement(cmd, cur->name,
1275 				     escap_userid,
1276 				     escap_realm,
1277 				     escap_passwd ?
1278 				     "<omitted>" : SQL_NULL_VALUE,
1279 				     sparams->utils);
1280 	    sparams->utils->log(sparams->utils->conn, SASL_LOG_DEBUG,
1281 				"sql plugin doing statement %s\n",
1282 				log_statement);
1283 	    sparams->utils->free(log_statement);
1284 	}
1285 
1286 	/* run the statement */
1287 	if (settings->sql_engine->sql_exec(conn, statement, NULL, 0, NULL,
1288 					   sparams->utils)) {
1289 	    ret = SASL_FAIL;
1290 	}
1291 
1292 	sparams->utils->free(statement);
1293     }
1294     if (ret != SASL_OK) {
1295 	sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR,
1296 			    "Failed to store auxprop; aborting transaction\n");
1297 	if (settings->sql_engine->sql_rollback_txn(conn, sparams->utils)) {
1298 	    sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR,
1299 				"Unable to rollback transaction\n");
1300 	}
1301     }
1302     else if (settings->sql_engine->sql_commit_txn(conn, sparams->utils)) {
1303 	sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR,
1304 			    "Unable to commit transaction\n");
1305     }
1306 
1307   done:
1308     if (escap_userid) sparams->utils->free(escap_userid);
1309     if (escap_realm) sparams->utils->free(escap_realm);
1310     if (escap_passwd) sparams->utils->free(escap_passwd);
1311     if (conn) settings->sql_engine->sql_close(conn);
1312     if (userid) sparams->utils->free(userid);
1313     if (realm) sparams->utils->free(realm);
1314     if (user_buf) sparams->utils->free(user_buf);
1315 
1316     return ret;
1317 
1318     /* do a little dance */
1319 }
1320 
1321 
sql_auxprop_free(void * glob_context,const sasl_utils_t * utils)1322 static void sql_auxprop_free(void *glob_context, const sasl_utils_t *utils)
1323 {
1324     sql_settings_t *settings;
1325 
1326     settings = (sql_settings_t *)glob_context;
1327 
1328     if (!settings) return;
1329 
1330     utils->log(utils->conn, SASL_LOG_DEBUG, "sql freeing memory\n");
1331 
1332     utils->free(settings);
1333 }
1334 
1335 static sasl_auxprop_plug_t sql_auxprop_plugin = {
1336     0,			/* Features */
1337     0,			/* spare */
1338     NULL,		/* glob_context */
1339     sql_auxprop_free,	/* auxprop_free */
1340     sql_auxprop_lookup,	/* auxprop_lookup */
1341     "sql",		/* name */
1342     sql_auxprop_store	/* auxprop_store */
1343 };
1344 
sql_auxprop_plug_init(const sasl_utils_t * utils,int max_version,int * out_version,sasl_auxprop_plug_t ** plug,const char * plugname)1345 int sql_auxprop_plug_init(const sasl_utils_t *utils,
1346 			  int max_version,
1347 			  int *out_version,
1348 			  sasl_auxprop_plug_t **plug,
1349 			  const char *plugname __attribute__((unused)))
1350 {
1351     sql_settings_t *settings;
1352 
1353     if (!out_version || !plug) return SASL_BADPARAM;
1354 
1355     if (max_version < SASL_AUXPROP_PLUG_VERSION) return SASL_BADVERS;
1356     *out_version = SASL_AUXPROP_PLUG_VERSION;
1357 
1358     *plug = &sql_auxprop_plugin;
1359 
1360     settings = (sql_settings_t *) utils->malloc(sizeof(sql_settings_t));
1361 
1362     if (!settings) {
1363 	MEMERROR(utils);
1364 	return SASL_NOMEM;
1365     }
1366 
1367     memset(settings, 0, sizeof(sql_settings_t));
1368     sql_get_settings(utils, settings);
1369 
1370     if (!settings->sql_engine->name) return SASL_NOMECH;
1371 
1372     if (!sql_exists(settings->sql_select)) {
1373 	utils->log(utils->conn, SASL_LOG_ERR, "sql_select option missing");
1374 	utils->free(settings);
1375 	return SASL_NOMECH;
1376     }
1377 
1378     utils->log(utils->conn, SASL_LOG_DEBUG,
1379 	       "sql auxprop plugin using %s engine\n",
1380 	       settings->sql_engine->name);
1381 
1382     sql_auxprop_plugin.glob_context = settings;
1383 
1384     return SASL_OK;
1385 }
1386