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