1 /*
2 * ProFTPD: mod_sql -- SQL frontend
3 * Copyright (c) 1998-1999 Johnie Ingram.
4 * Copyright (c) 2001 Andrew Houghton.
5 * Copyright (c) 2004-2020 TJ Saunders
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
20 *
21 * As a special exemption, Andrew Houghton and other respective copyright
22 * holders give permission to link this program with OpenSSL, and distribute
23 * the resulting executable, without including the source code for OpenSSL in
24 * the source distribution.
25 */
26
27 #include "conf.h"
28 #include "privs.h"
29 #include "mod_sql.h"
30 #include "jot.h"
31
32 #define MOD_SQL_VERSION "mod_sql/4.5"
33
34 #if defined(HAVE_CRYPT_H) && !defined(AIX4) && !defined(AIX5)
35 # include <crypt.h>
36 #endif
37
38 #if defined(HAVE_OPENSSL) || defined(PR_USE_OPENSSL)
39 # include <openssl/evp.h>
40 #endif
41
42 /* default information for tables and fields */
43 #define MOD_SQL_DEF_USERTABLE "users"
44 #define MOD_SQL_DEF_USERNAMEFIELD "userid"
45 #define MOD_SQL_DEF_USERUIDFIELD "uid"
46 #define MOD_SQL_DEF_USERGIDFIELD "gid"
47 #define MOD_SQL_DEF_USERPASSWORDFIELD "passwd"
48 #define MOD_SQL_DEF_USERSHELLFIELD "shell"
49 #define MOD_SQL_DEF_USERHOMEDIRFIELD "homedir"
50
51 #define MOD_SQL_DEF_GROUPTABLE "groups"
52 #define MOD_SQL_DEF_GROUPNAMEFIELD "groupname"
53 #define MOD_SQL_DEF_GROUPGIDFIELD "gid"
54 #define MOD_SQL_DEF_GROUPMEMBERSFIELD "members"
55
56 /* default minimum ID / default UID / default GID info.
57 * UIDs and GIDs less than MOD_SQL_MIN_USER_UID and MOD_SQL_MIN_USER_GID,
58 * respectively, get automatically mapped to the defaults, below. These can
59 * be overridden using directives
60 */
61 #define MOD_SQL_MIN_USER_UID 999
62 #define MOD_SQL_MIN_USER_GID 999
63 #define MOD_SQL_DEF_UID 65533
64 #define MOD_SQL_DEF_GID 65533
65
66 #define MOD_SQL_BUFSIZE 32
67
68 /* Named Query defines */
69 #define SQL_SELECT_C "SELECT"
70 #define SQL_INSERT_C "INSERT"
71 #define SQL_UPDATE_C "UPDATE"
72 #define SQL_FREEFORM_C "FREEFORM"
73
74 /* SQLEngine flags */
75 #define SQL_ENGINE_FL_AUTH 0x001
76 #define SQL_ENGINE_FL_LOG 0x002
77
78 /* SQLLog flags */
79 #define SQL_LOG_FL_IGNORE_ERRORS 0x001
80
81 /* authmask defines */
82 #define SQL_AUTH_USERS (1<<0)
83 #define SQL_AUTH_GROUPS (1<<1)
84 #define SQL_AUTH_USERSET (1<<4)
85 #define SQL_AUTH_GROUPSET (1<<5)
86 #define SQL_FAST_USERSET (1<<6)
87 #define SQL_FAST_GROUPSET (1<<7)
88
89 #define SQL_GROUPS (cmap.authmask & SQL_AUTH_GROUPS)
90 #define SQL_USERS (cmap.authmask & SQL_AUTH_USERS)
91 #define SQL_GROUPSET (cmap.authmask & SQL_AUTH_GROUPSET)
92 #define SQL_USERSET (cmap.authmask & SQL_AUTH_USERSET)
93 #define SQL_FASTGROUPS (cmap.authmask & SQL_FAST_GROUPSET)
94 #define SQL_FASTUSERS (cmap.authmask & SQL_FAST_USERSET)
95
96 /*
97 * externs, function signatures.. whatever necessary to make
98 * the compiler happy..
99 */
100
101 module sql_module;
102
103 unsigned long pr_sql_opts = 0UL;
104 unsigned int pr_sql_conn_policy = 0;
105
106 /* For tracking the size of deleted files. */
107 static off_t sql_dele_filesz = 0;
108
109 static int sql_keepalive_timer_id = -1;
110 static const char *sql_keepalive_stmt = NULL;
111
112 /* It is best if this value is larger than the PR_TUNABLE_BUFFER_SIZE value.
113 * PR_TUNABLE_BUFFER_SIZE controls how much network data from a client at
114 * a time we might read; by keeping the statement buffer size larger, we reduce
115 * the chance of handling data from the network which exceeds the statement
116 * buffer length.
117 */
118 #define SQL_MAX_STMT_LEN 4096
119
120 static int sql_sess_init(void);
121
122 static char *sql_prepare_where(int, cmd_rec *, int, ...);
123 #define SQL_PREPARE_WHERE_FL_NO_TAGS 0x00001
124
125 static int resolve_numeric_val(cmd_rec *cmd, const char *val);
126
127 static modret_t *process_named_query(cmd_rec *cmd, char *name, int flags);
128 static const char *get_named_conn_backend(const char *name);
129 static char *get_query_named_conn(config_rec *c);
130 static void set_named_conn_backend(const char *name);
131
132 MODRET sql_auth_getgrent(cmd_rec *cmd);
133 MODRET sql_auth_setgrent(cmd_rec *cmd);
134 MODRET sql_lookup(cmd_rec *cmd);
135
136 static cmdtable *sql_set_backend(const char *name);
137
138 static pool *sql_pool = NULL;
139 static const char *trace_channel = "sql";
140
141 /*
142 * cache typedefs
143 */
144
145 #define CACHE_SIZE 13
146
147 typedef struct cache_entry {
148 struct cache_entry *list_next;
149 struct cache_entry *bucket_next;
150 void *data;
151 } cache_entry_t;
152
153 /* This struct holds invariant information for the current session */
154
155 static struct {
156 /* Info valid after getpwnam(). */
157 char *authuser; /* current authorized user */
158 struct passwd *authpasswd; /* and their passwd struct */
159
160 /* Generic status information. */
161 int engine; /* is mod_sql on? */
162 int authmask; /* authentication mask.
163 * see set_sqlauthenticate for info */
164 /* User table and field information. */
165 char *usrtable; /* user info table name */
166 char *usrfield; /* user name field */
167 char *pwdfield; /* user password field */
168 char *uidfield; /* user UID field */
169 char *gidfield; /* user GID field */
170 char *homedirfield; /* user homedir field */
171 char *shellfield; /* user login shell field */
172 char *userwhere; /* user where clause */
173
174 char *usercustom; /* custom users query (by name) */
175 char *usercustombyid; /* custom users query (by UID) */
176 char *usercustomuserset; /* custom query to get 'userset' users */
177 char *usercustomusersetfast; /* custom query to get 'usersetfast' users */
178
179 /* Group table and field information. */
180 char *grptable; /* group info table name */
181 char *grpfield; /* group name field */
182 char *grpgidfield; /* group GID field */
183 char *grpmembersfield; /* group members field */
184 char *groupwhere; /* group where clause */
185
186 char *groupcustombyname; /* custom group query (by name) */
187 char *groupcustombyid; /* custom group query (by GID) */
188 char *groupcustommembers; /* custom group query (user members only) */
189 char *groupcustomgroupset; /* custom query to get 'groupset' groups */
190 char *groupcustomgroupsetfast;/* custom query to get 'groupsetfast' groups */
191
192 /* Other information. */
193 array_header *auth_list; /* auth handler list */
194 char *defaulthomedir; /* default homedir if no field specified */
195
196 uid_t minid; /* users UID must be this or greater */
197 uid_t minuseruid; /* users UID must be this or greater */
198 gid_t minusergid; /* users UID must be this or greater */
199 uid_t defaultuid; /* default UID if none in database */
200 gid_t defaultgid; /* default GID if none in database */
201
202 cache_entry_t *curr_group; /* next group in group array for getgrent */
203 cache_entry_t *curr_passwd; /* next passwd in passwd array for getpwent */
204 int group_cache_filled;
205 int passwd_cache_filled;
206
207 /* Cache negative, as well as positive, lookups */
208 unsigned char negative_cache;
209
210 /* mod_ratio data -- someday this needs to be removed from mod_sql. */
211 char *sql_fstor; /* fstor int(11) NOT NULL DEFAULT '0', */
212 char *sql_fretr; /* fretr int(11) NOT NULL DEFAULT '0', */
213 char *sql_bstor; /* bstor int(11) NOT NULL DEFAULT '0', */
214 char *sql_bretr; /* bretr int(11) NOT NULL DEFAULT '0', */
215
216 char *sql_frate; /* frate int(11) NOT NULL DEFAULT '5', */
217 char *sql_fcred; /* fcred int(2) NOT NULL DEFAULT '15', */
218 char *sql_brate; /* brate int(11) NOT NULL DEFAULT '5', */
219 char *sql_bcred; /* bcred int(2) NOT NULL DEFAULT '150000', */
220
221 /* Precomputed strings. */
222 char *usrfields;
223 char *grpfields;
224 } cmap;
225
226 /* For handling the SQLBackend directive */
227 struct sql_backend {
228 struct sql_backend *next, *prev;
229 const char *backend;
230 cmdtable *cmdtab;
231 };
232
233 static struct sql_backend *sql_backends = NULL;
234 static unsigned int sql_nbackends = 0;
235 static cmdtable *sql_cmdtable = NULL, *sql_default_cmdtable = NULL;
236
237 /* For handling the SQLNamedConnectInfo directives */
238 struct sql_named_conn {
239 struct sql_named_conn *next, *prev;
240 const char *conn_name;
241 unsigned int conn_policy;
242 const char *backend;
243 };
244
245 static struct sql_named_conn *sql_named_conns = NULL;
246
247 #define MOD_SQL_DEF_CONN_NAME "default"
248
249 /* Look up the backend to use for the named connection. */
get_named_conn_backend(const char * conn_name)250 static const char *get_named_conn_backend(const char *conn_name) {
251 if (conn_name == NULL) {
252 errno = EINVAL;
253 return NULL;
254 }
255
256 /* Make sure that "default" is handled as a reserved connection name. */
257 if (strcmp(conn_name, MOD_SQL_DEF_CONN_NAME) == 0) {
258 errno = EACCES;
259 return NULL;
260 }
261
262 if (sql_named_conns != NULL) {
263 struct sql_named_conn *snc;
264
265 for (snc = sql_named_conns; snc; snc = snc->next) {
266 pr_trace_msg(trace_channel, 17,
267 "comparing requested named connection '%s' with '%s'", conn_name,
268 snc->conn_name);
269
270 if (strcmp(snc->conn_name, conn_name) == 0) {
271 return snc->backend;
272 }
273 }
274
275 pr_trace_msg(trace_channel, 17,
276 "unable to find named connection '%s': no such named connection found",
277 conn_name);
278
279 } else {
280 pr_trace_msg(trace_channel, 17,
281 "unable to find named connection '%s': no named connections registered",
282 conn_name);
283 }
284
285 errno = ENOENT;
286 return NULL;
287 }
288
get_query_named_conn(config_rec * c)289 static char *get_query_named_conn(config_rec *c) {
290 char *conn_name = MOD_SQL_DEF_CONN_NAME;
291
292 if (strcasecmp(c->argv[0], SQL_SELECT_C) == 0 ||
293 strcasecmp(c->argv[0], SQL_FREEFORM_C) == 0) {
294 conn_name = c->argv[2];
295
296 } else if (strcasecmp(c->argv[0], SQL_INSERT_C) == 0 ||
297 strcasecmp(c->argv[0], SQL_UPDATE_C) == 0) {
298 conn_name = c->argv[3];
299 }
300
301 return conn_name;
302 }
303
set_named_conn_backend(const char * conn_name)304 static void set_named_conn_backend(const char *conn_name) {
305 const char *backend;
306
307 if (conn_name == NULL) {
308 /* The caller is requesting that we request the backend to the default. */
309 sql_cmdtable = sql_default_cmdtable;
310 return;
311 }
312
313 if (strcmp(conn_name, MOD_SQL_DEF_CONN_NAME) == 0) {
314 sql_cmdtable = sql_default_cmdtable;
315 return;
316 }
317
318 backend = get_named_conn_backend(conn_name);
319 if (backend == NULL) {
320 sql_log(DEBUG_INFO, MOD_SQL_VERSION
321 ": named connection '%s' does not exist, defaulting to '%s'",
322 conn_name, MOD_SQL_DEF_CONN_NAME);
323 sql_cmdtable = sql_default_cmdtable;
324 return;
325 }
326
327 if (sql_set_backend(backend) == NULL) {
328 sql_log(DEBUG_INFO, "unable to load SQL backend '%s': %s", backend,
329 strerror(errno));
330
331 } else {
332 sql_log(DEBUG_INFO, "using named connection '%s', backend '%s' for query",
333 conn_name, backend);
334 }
335 }
336
337 /*
338 * cache functions
339 */
340
341 typedef unsigned int (* val_func)(const void *);
342 typedef int (* cmp_func)(const void *, const void *);
343
344 typedef struct {
345 /* memory pool for this object */
346 pool *pool;
347
348 /* cache buckets */
349 cache_entry_t *buckets[ CACHE_SIZE ];
350
351 /* cache functions */
352 val_func hash_val;
353 cmp_func cmp;
354
355 /* list pointers */
356 cache_entry_t *head;
357
358 /* list size */
359 unsigned int nelts;
360 } cache_t;
361
362 static cache_t *group_name_cache = NULL;
363 static cache_t *group_gid_cache = NULL;
364 static cache_t *passwd_name_cache = NULL;
365 static cache_t *passwd_uid_cache = NULL;
366
make_cache(pool * p,val_func hash_val,cmp_func cmp)367 static cache_t *make_cache(pool *p, val_func hash_val, cmp_func cmp) {
368 cache_t *res;
369
370 if (p == NULL ||
371 hash_val == NULL ||
372 cmp == NULL)
373 return NULL;
374
375 res = (cache_t *) pcalloc(p, sizeof(cache_t));
376
377 res->pool = p;
378 res->hash_val = hash_val;
379 res->cmp = cmp;
380
381 res->head = NULL;
382
383 res->nelts = 0;
384
385 return res;
386 }
387
cache_addentry(cache_t * cache,void * data)388 static cache_entry_t *cache_addentry(cache_t *cache, void *data) {
389 cache_entry_t *entry;
390 int hashval;
391
392 if (cache == NULL ||
393 data == NULL)
394 return NULL;
395
396 /* create the entry */
397 entry = (cache_entry_t *) pcalloc(cache->pool, sizeof(cache_entry_t));
398 entry->data = data;
399
400 /* deal with the list */
401
402 if (cache->head == NULL) {
403 cache->head = entry;
404
405 } else {
406 entry->list_next = cache->head;
407 cache->head = entry;
408 }
409
410 /* deal with the buckets */
411 hashval = cache->hash_val(data) % CACHE_SIZE;
412 if (cache->buckets[hashval] == NULL) {
413 cache->buckets[hashval] = entry;
414
415 } else {
416 entry->bucket_next = cache->buckets[hashval];
417 cache->buckets[hashval] = entry;
418 }
419
420 cache->nelts++;
421
422 return entry;
423 }
424
cache_findvalue(cache_t * cache,void * data)425 static void *cache_findvalue(cache_t *cache, void *data) {
426 cache_entry_t *entry;
427 int hashval;
428
429 if (cache == NULL ||
430 data == NULL) {
431 errno = EINVAL;
432 return NULL;
433 }
434
435 hashval = cache->hash_val(data) % CACHE_SIZE;
436
437 entry = cache->buckets[hashval];
438 while (entry != NULL) {
439 pr_signals_handle();
440
441 if (cache->cmp(data, entry->data)) {
442 break;
443 }
444
445 entry = entry->bucket_next;
446 }
447
448 return (entry == NULL ? NULL : entry->data);
449 }
450
sql_make_cmd(pool * p,int argc,...)451 cmd_rec *sql_make_cmd(pool *p, int argc, ...) {
452 register int i = 0;
453 pool *newpool = NULL;
454 cmd_rec *cmd = NULL;
455 va_list args;
456
457 if (argc < 0) {
458 errno = EINVAL;
459 return NULL;
460 }
461
462 newpool = make_sub_pool(p);
463 cmd = pcalloc(newpool, sizeof(cmd_rec));
464 cmd->argc = argc;
465 cmd->stash_index = -1;
466 cmd->pool = newpool;
467
468 cmd->argv = pcalloc(newpool, sizeof(void *) * (argc + 1));
469 cmd->tmp_pool = newpool;
470 cmd->server = main_server;
471
472 va_start(args, argc);
473
474 for (i = 0; i < argc; i++) {
475 cmd->argv[i] = (void *) va_arg(args, char *);
476 }
477 va_end(args);
478
479 cmd->argv[argc] = NULL;
480 return cmd;
481 }
482
check_response(modret_t * mr,int flags)483 static int check_response(modret_t *mr, int flags) {
484 if (!MODRET_ISERROR(mr)) {
485 return 0;
486 }
487
488 sql_log(DEBUG_WARN, "%s", "unrecoverable backend error");
489 sql_log(DEBUG_WARN, "error: '%s'", mr->mr_numeric);
490 sql_log(DEBUG_WARN, "message: '%s'", mr->mr_message);
491
492 pr_log_pri(PR_LOG_ERR, MOD_SQL_VERSION
493 ": unrecoverable backend error: (%s) %s", mr->mr_numeric, mr->mr_message);
494 pr_log_pri(PR_LOG_ERR, MOD_SQL_VERSION
495 ": check the SQLLogFile for more details");
496
497 pr_event_generate("mod_sql.db.error", mr->mr_message);
498
499 if (!(flags & SQL_LOG_FL_IGNORE_ERRORS) &&
500 !(pr_sql_opts & SQL_OPT_NO_DISCONNECT_ON_ERROR)) {
501 pr_session_disconnect(&sql_module, PR_SESS_DISCONNECT_BY_APPLICATION,
502 "Database error");
503 }
504
505 if (flags & SQL_LOG_FL_IGNORE_ERRORS) {
506 sql_log(DEBUG_FUNC, "SQLLog IGNORE_ERRORS in effect, not exiting");
507
508 } else {
509 sql_log(DEBUG_FUNC, "SQLOption noDisconnectOnError in effect, not exiting");
510 }
511
512 return -1;
513 }
514
sql_dispatch(cmd_rec * cmd,char * cmdname)515 static modret_t *sql_dispatch(cmd_rec *cmd, char *cmdname) {
516 modret_t *mr = NULL;
517 register unsigned int i = 0;
518
519 pr_trace_msg(trace_channel, 19, "dispatching SQL command '%s'", cmdname);
520
521 for (i = 0; sql_cmdtable[i].command; i++) {
522 if (strcmp(cmdname, sql_cmdtable[i].command) == 0) {
523 pr_signals_block();
524 mr = sql_cmdtable[i].handler(cmd);
525 pr_signals_unblock();
526 return mr;
527 }
528 }
529
530 sql_log(DEBUG_WARN, "unknown backend handler '%s'", cmdname);
531 return PR_ERROR(cmd);
532 }
533
sql_get_backend(const char * backend)534 static struct sql_backend *sql_get_backend(const char *backend) {
535 struct sql_backend *sb;
536
537 if (sql_backends == NULL) {
538 pr_trace_msg(trace_channel, 17,
539 "unable to find '%s' backend: no backends registered", backend);
540 return NULL;
541 }
542
543 for (sb = sql_backends; sb; sb = sb->next) {
544 pr_trace_msg(trace_channel, 17,
545 "comparing requested backend '%s' with '%s'", backend, sb->backend);
546
547 if (strcasecmp(sb->backend, backend) == 0) {
548 return sb;
549 }
550 }
551
552 pr_trace_msg(trace_channel, 17,
553 "unable to find '%s' backend: no such backend found", backend);
554 errno = ENOENT;
555 return NULL;
556 }
557
558 /* This function is used by mod_sql backends, to register their
559 * individual backend command tables with the main mod_sql module.
560 */
sql_register_backend(const char * backend,cmdtable * cmdtab)561 int sql_register_backend(const char *backend, cmdtable *cmdtab) {
562 struct sql_backend *sb;
563
564 if (!backend || !cmdtab) {
565 errno = EINVAL;
566 return -1;
567 }
568
569 if (!sql_pool) {
570 sql_pool = make_sub_pool(permanent_pool);
571 pr_pool_tag(sql_pool, MOD_SQL_VERSION);
572 }
573
574 /* Check to see if this backend has already been registered. */
575 sb = sql_get_backend(backend);
576 if (sb) {
577 errno = EEXIST;
578 return -1;
579 }
580
581 sb = pcalloc(sql_pool, sizeof(struct sql_backend));
582 sb->backend = backend;
583 sb->cmdtab = cmdtab;
584
585 if (sql_backends) {
586 sql_backends->prev = sb;
587 sb->next = sql_backends;
588 }
589
590 sql_backends = sb;
591 sql_nbackends++;
592 pr_trace_msg(trace_channel, 8, "registered '%s' backend", backend);
593
594 return 0;
595 }
596
597 /* Used by mod_sql backends to unregister their backend command tables
598 * from the main mod_sql module.
599 */
sql_unregister_backend(const char * backend)600 int sql_unregister_backend(const char *backend) {
601 struct sql_backend *sb;
602
603 if (!backend) {
604 errno = EINVAL;
605 return -1;
606 }
607
608 /* Check to see if this backend has been registered. */
609 sb = sql_get_backend(backend);
610 if (!sb) {
611 errno = ENOENT;
612 return -1;
613 }
614
615 #if !defined(PR_SHARED_MODULE)
616 /* If there is only one registered backend, it cannot be removed.
617 */
618 if (sql_nbackends == 1) {
619 errno = EPERM;
620 return -1;
621 }
622
623 /* Be sure to handle the case where this is the currently active backend. */
624 if (sql_cmdtable &&
625 sb->cmdtab == sql_cmdtable) {
626 errno = EACCES;
627 return -1;
628 }
629 #endif
630
631 /* Remove this backend from the linked list. */
632 if (sb->prev) {
633 sb->prev->next = sb->next;
634
635 } else {
636 /* This backend is the start of the sql_backends list (prev is NULL),
637 * so we need to update the list head pointer as well.
638 */
639 sql_backends = sb->next;
640 }
641
642 if (sb->next)
643 sb->next->prev = sb->prev;
644
645 sb->prev = sb->next = NULL;
646
647 sql_nbackends--;
648
649 /* NOTE: a counter should be kept of the number of unregistrations,
650 * as the memory for a registration is not freed on unregistration.
651 */
652
653 return 0;
654 }
655
656 /* Determine which backend to use.
657 *
658 * If there is only one registered backend to use, the decision is easy.
659 *
660 * If there are more than one backends, default to using the first
661 * entry in the linked list (last backend module registered). Check
662 * for a given backend name argument, if any, to see if that backend
663 * is available.
664 */
sql_set_backend(const char * backend)665 static cmdtable *sql_set_backend(const char *backend) {
666 if (sql_nbackends == 0 ||
667 sql_backends == NULL) {
668 pr_log_debug(DEBUG0, MOD_SQL_VERSION ": no SQL backends registered");
669 sql_log(DEBUG_INFO, "%s", "no SQL backends registered");
670 errno = ENOENT;
671 return NULL;
672 }
673
674 if (sql_nbackends == 1) {
675 pr_log_debug(DEBUG8, MOD_SQL_VERSION ": defaulting to '%s' backend",
676 sql_backends->backend);
677 sql_log(DEBUG_INFO, "defaulting to '%s' backend", sql_backends->backend);
678 sql_cmdtable = sql_backends->cmdtab;
679
680 } else if (sql_nbackends > 1) {
681 if (backend) {
682 struct sql_backend *b;
683
684 for (b = sql_backends; b; b = b->next) {
685 if (strcasecmp(b->backend, backend) == 0) {
686 sql_log(DEBUG_INFO, "using SQLBackend '%s'", backend);
687 sql_cmdtable = b->cmdtab;
688 break;
689 }
690 }
691
692 /* If no match is found, default to using the last entry in the list. */
693 if (!sql_cmdtable) {
694 b = sql_backends;
695 while (b->next != NULL) {
696 pr_signals_handle();
697 b = b->next;
698 }
699
700 sql_log(DEBUG_INFO,
701 "SQLBackend '%s' not found, defaulting to '%s' backend",
702 backend, b->backend);
703 sql_cmdtable = b->cmdtab;
704 }
705
706 } else {
707 /* Default to using the last entry in the list. */
708 struct sql_backend *b = sql_backends;
709
710 while (b->next != NULL) {
711 pr_signals_handle();
712 b = b->next;
713 }
714
715 sql_log(DEBUG_INFO, "defaulting to '%s' backend",
716 b->backend);
717 sql_cmdtable = b->cmdtab;
718 }
719 }
720
721 return sql_cmdtable;
722 }
723
724 /* Text resolvers. */
725
726 struct sql_resolved {
727 char *ptr, *buf;
728 size_t bufsz, buflen;
729
730 /* Used for escaping the resolved values per the database rules. */
731 const char *conn_name;
732 int conn_flags;
733 };
734
is_escaped_text(const char * text,size_t text_len)735 static int is_escaped_text(const char *text, size_t text_len) {
736 register unsigned int i;
737
738 if (text[0] != '\'') {
739 return FALSE;
740 }
741
742 if (text[text_len-1] != '\'') {
743 return FALSE;
744 }
745
746 for (i = 1; i < text_len-1; i++) {
747 if (text[i] == '\'') {
748 return FALSE;
749 }
750 }
751
752 return TRUE;
753 }
754
sql_resolved_append_text(pool * p,struct sql_resolved * resolved,const char * text,size_t text_len)755 static int sql_resolved_append_text(pool *p, struct sql_resolved *resolved,
756 const char *text, size_t text_len) {
757 char *new_text;
758 size_t new_textlen;
759
760 if (text == NULL ||
761 text_len == 0) {
762 return 0;
763 }
764
765 /* For backward compatibility (see Issue #1149), we indulge in a little
766 * heuristic here, and only escape the text if it hasn't already been
767 * escaped. How to properly tell? If the first and last characters of
768 * the given text are `'`, AND there are no other occurrences of that
769 * character in the text, assume it has already been quoted.
770 */
771 if (is_escaped_text(text, text_len) == FALSE) {
772 modret_t *mr;
773
774 mr = sql_dispatch(sql_make_cmd(p, 2, resolved->conn_name, text),
775 "sql_escapestring");
776 if (check_response(mr, resolved->conn_flags) < 0) {
777 errno = EIO;
778 return -1;
779 }
780
781 new_text = (char *) mr->data;
782 new_textlen = strlen(new_text);
783
784 } else {
785 pr_trace_msg(trace_channel, 17,
786 "text '%s' is already escaped, skipping escaping it again", text);
787
788 new_text = (char *) text;
789 new_textlen = text_len;
790 }
791
792 if (new_textlen > resolved->buflen) {
793 new_textlen = resolved->buflen;
794 }
795
796 pr_trace_msg(trace_channel, 19, "appending text '%s' (%lu) to buffer",
797 new_text, (unsigned long) new_textlen);
798 memcpy(resolved->buf, new_text, new_textlen);
799 resolved->buf += new_textlen;
800 resolved->buflen -= new_textlen;
801
802 return 0;
803 }
804
sql_resolve_on_meta(pool * p,pr_jot_ctx_t * jot_ctx,unsigned char logfmt_id,const char * jot_hint,const void * val)805 static int sql_resolve_on_meta(pool *p, pr_jot_ctx_t *jot_ctx,
806 unsigned char logfmt_id, const char *jot_hint, const void *val) {
807 int res = 0;
808 struct sql_resolved *resolved;
809
810 resolved = jot_ctx->log;
811 if (resolved->buflen > 0) {
812 const char *text = NULL;
813 size_t text_len = 0;
814 char buf[1024];
815
816 switch (logfmt_id) {
817 case LOGFMT_META_MICROSECS: {
818 unsigned long num;
819
820 num = *((double *) val);
821 text_len = pr_snprintf(buf, sizeof(buf)-1, "%06lu", num);
822 buf[text_len] = '\0';
823 text = buf;
824 break;
825 }
826
827 case LOGFMT_META_MILLISECS: {
828 unsigned long num;
829
830 num = *((double *) val);
831 text_len = pr_snprintf(buf, sizeof(buf)-1, "%03lu", num);
832 buf[text_len] = '\0';
833 text = buf;
834 break;
835 }
836
837 case LOGFMT_META_LOCAL_PORT:
838 case LOGFMT_META_REMOTE_PORT:
839 case LOGFMT_META_RESPONSE_CODE:
840 case LOGFMT_META_XFER_PORT: {
841 int num;
842
843 num = (int) *((double *) val);
844 text_len = pr_snprintf(buf, sizeof(buf)-1, "%d", num);
845 buf[text_len] = '\0';
846 text = buf;
847 break;
848 }
849
850 case LOGFMT_META_UID: {
851 uid_t uid;
852
853 uid = *((double *) val);
854 text = pr_uid2str(p, uid);
855 break;
856 }
857
858 case LOGFMT_META_GID: {
859 gid_t gid;
860
861 gid = *((double *) val);
862 text = pr_gid2str(p, gid);
863 break;
864 }
865
866 case LOGFMT_META_BYTES_SENT:
867 case LOGFMT_META_FILE_OFFSET:
868 case LOGFMT_META_FILE_SIZE:
869 case LOGFMT_META_RAW_BYTES_IN:
870 case LOGFMT_META_RAW_BYTES_OUT:
871 case LOGFMT_META_RESPONSE_MS:
872 case LOGFMT_META_XFER_MS: {
873 off_t num;
874
875 num = *((double *) val);
876 text_len = pr_snprintf(buf, sizeof(buf)-1, "%" PR_LU, (pr_off_t) num);
877 buf[text_len] = '\0';
878 text = buf;
879 break;
880 }
881
882 case LOGFMT_META_EPOCH:
883 case LOGFMT_META_PID: {
884 unsigned long num;
885
886 num = *((double *) val);
887 text_len = pr_snprintf(buf, sizeof(buf)-1, "%lu", num);
888 buf[text_len] = '\0';
889 text = buf;
890 break;
891 }
892
893 case LOGFMT_META_FILE_MODIFIED: {
894 int truth;
895
896 truth = *((int *) val);
897 text = truth ? "true" : "false";
898 break;
899 }
900
901 case LOGFMT_META_SECONDS: {
902 float num;
903
904 num = *((double *) val);
905 text_len = pr_snprintf(buf, sizeof(buf)-1, "%0.3f", num);
906 buf[text_len] = '\0';
907 text = buf;
908 break;
909 }
910
911 /* Jot uses gmtime; for backward compatibility, we need to use
912 * localtime.
913 */
914 case LOGFMT_META_TIME: {
915 const char *time_fmt = "%Y-%m-%d %H:%M:%S %z";
916 struct tm *tm;
917 time_t now;
918
919 now = time(NULL);
920 tm = pr_localtime(p, &now);
921
922 if (jot_hint != NULL) {
923 time_fmt = jot_hint;
924 }
925
926 text_len = strftime(buf, sizeof(buf)-1, time_fmt, tm);
927 buf[text_len] = '\0';
928 text = buf;
929 break;
930 }
931
932 case LOGFMT_META_CUSTOM: {
933 register unsigned int i;
934 const char *val_text;
935 size_t val_textlen;
936 cmd_rec *cmd;
937 int is_numeric_tag = TRUE;
938
939 cmd = (cmd_rec *) jot_ctx->user_data;
940 val_text = (const char *) val;
941 val_textlen = strlen(val_text);
942
943 for (i = 0; i < val_textlen-1; i++) {
944 if (!PR_ISDIGIT(val_text[i])) {
945 is_numeric_tag = FALSE;
946 break;
947 }
948 }
949
950 if (is_numeric_tag) {
951 int idx;
952
953 idx = resolve_numeric_val(cmd, val_text);
954 if (idx < 0) {
955 sql_log(DEBUG_FUNC, "out-of-bounds numeric reference in query");
956 errno = EIO;
957 return -1;
958 }
959
960 text = cmd->argv[idx+2];
961 }
962
963 break;
964 }
965
966 case LOGFMT_META_ANON_PASS:
967 case LOGFMT_META_BASENAME:
968 case LOGFMT_META_CLASS:
969 case LOGFMT_META_CMD_PARAMS:
970 case LOGFMT_META_COMMAND:
971 case LOGFMT_META_DIR_NAME:
972 case LOGFMT_META_DIR_PATH:
973 case LOGFMT_META_ENV_VAR:
974 case LOGFMT_META_EOS_REASON:
975 case LOGFMT_META_FILENAME:
976 case LOGFMT_META_GROUP:
977 case LOGFMT_META_IDENT_USER:
978 case LOGFMT_META_ISO8601:
979 case LOGFMT_META_LOCAL_FQDN:
980 case LOGFMT_META_LOCAL_IP:
981 case LOGFMT_META_LOCAL_NAME:
982 case LOGFMT_META_METHOD:
983 case LOGFMT_META_NOTE_VAR:
984 case LOGFMT_META_ORIGINAL_USER:
985 case LOGFMT_META_PROTOCOL:
986 case LOGFMT_META_REMOTE_HOST:
987 case LOGFMT_META_REMOTE_IP:
988 case LOGFMT_META_RENAME_FROM:
989 case LOGFMT_META_RESPONSE_STR:
990 case LOGFMT_META_USER:
991 case LOGFMT_META_VERSION:
992 case LOGFMT_META_VHOST_IP:
993 case LOGFMT_META_XFER_FAILURE:
994 case LOGFMT_META_XFER_PATH:
995 case LOGFMT_META_XFER_STATUS:
996 case LOGFMT_META_XFER_TYPE:
997 default:
998 text = val;
999 break;
1000 }
1001
1002 if (text != NULL &&
1003 text_len == 0) {
1004 text_len = strlen(text);
1005 }
1006
1007 res = sql_resolved_append_text(p, resolved, text, text_len);
1008 }
1009
1010 return res;
1011 }
1012
sql_resolve_on_default(pool * p,pr_jot_ctx_t * jot_ctx,unsigned char logfmt_id)1013 static int sql_resolve_on_default(pool *p, pr_jot_ctx_t *jot_ctx,
1014 unsigned char logfmt_id) {
1015 int res = 0;
1016 struct sql_resolved *resolved;
1017
1018 resolved = jot_ctx->log;
1019 if (resolved->buflen > 0) {
1020 const char *text = NULL;
1021 size_t text_len = 0;
1022
1023 switch (logfmt_id) {
1024 case LOGFMT_META_ANON_PASS:
1025 case LOGFMT_META_IDENT_USER:
1026 text = "UNKNOWN";
1027 text_len = strlen(text);
1028 break;
1029
1030 case LOGFMT_META_SECONDS:
1031 text = "0.0";
1032 text_len = strlen(text);
1033 break;
1034
1035 case LOGFMT_META_BASENAME:
1036 case LOGFMT_META_BYTES_SENT:
1037 case LOGFMT_META_CLASS:
1038 case LOGFMT_META_FILENAME:
1039 case LOGFMT_META_FILE_OFFSET:
1040 case LOGFMT_META_FILE_SIZE:
1041 case LOGFMT_META_GROUP:
1042 case LOGFMT_META_ORIGINAL_USER:
1043 case LOGFMT_META_RENAME_FROM:
1044 case LOGFMT_META_RESPONSE_CODE:
1045 case LOGFMT_META_RESPONSE_MS:
1046 case LOGFMT_META_RESPONSE_STR:
1047 case LOGFMT_META_USER:
1048 case LOGFMT_META_XFER_FAILURE:
1049 case LOGFMT_META_XFER_MS:
1050 case LOGFMT_META_XFER_PATH:
1051 case LOGFMT_META_XFER_STATUS:
1052 case LOGFMT_META_XFER_TYPE:
1053 text = "-";
1054 text_len = 1;
1055 break;
1056
1057 /* These explicitly do NOT have default values. */
1058 case LOGFMT_META_CMD_PARAMS:
1059 case LOGFMT_META_COMMAND:
1060 case LOGFMT_META_DIR_NAME:
1061 case LOGFMT_META_DIR_PATH:
1062 case LOGFMT_META_ENV_VAR:
1063 case LOGFMT_META_EOS_REASON:
1064 case LOGFMT_META_NOTE_VAR:
1065 case LOGFMT_META_METHOD:
1066 default:
1067 break;
1068 }
1069
1070 res = sql_resolved_append_text(p, resolved, text, text_len);
1071 }
1072
1073 return res;
1074 }
1075
sql_resolve_on_other(pool * p,pr_jot_ctx_t * jot_ctx,unsigned char * text,size_t text_len)1076 static int sql_resolve_on_other(pool *p, pr_jot_ctx_t *jot_ctx,
1077 unsigned char *text, size_t text_len) {
1078 struct sql_resolved *resolved;
1079
1080 resolved = jot_ctx->log;
1081 if (resolved->buflen > 0) {
1082 pr_trace_msg(trace_channel, 19, "appending text '%.*s' (%lu) to buffer",
1083 (int) text_len, text, (unsigned long) text_len);
1084 memcpy(resolved->buf, text, text_len);
1085 resolved->buf += text_len;
1086 resolved->buflen -= text_len;
1087 }
1088
1089 return 0;
1090 }
1091
1092 /* Default SQL password handlers (a.k.a. "AuthTypes") provided by mod_sql. */
1093
sql_auth_crypt(cmd_rec * cmd,const char * plaintext,const char * ciphertext)1094 static modret_t *sql_auth_crypt(cmd_rec *cmd, const char *plaintext,
1095 const char *ciphertext) {
1096 char *res = NULL;
1097
1098 if (*ciphertext == '\0') {
1099 return PR_ERROR_INT(cmd, PR_AUTH_BADPWD);
1100 }
1101
1102 res = (char *) crypt(plaintext, ciphertext);
1103 if (res == NULL) {
1104 sql_log(DEBUG_WARN, "error using crypt(3): %s", strerror(errno));
1105 return PR_ERROR_INT(cmd, PR_AUTH_BADPWD);
1106 }
1107
1108 if (strcmp(res, ciphertext) == 0) {
1109 return PR_HANDLED(cmd);
1110 }
1111
1112 return PR_ERROR_INT(cmd, PR_AUTH_BADPWD);
1113 }
1114
sql_auth_plaintext(cmd_rec * cmd,const char * plaintext,const char * ciphertext)1115 static modret_t *sql_auth_plaintext(cmd_rec *cmd, const char *plaintext,
1116 const char *ciphertext) {
1117
1118 if (*ciphertext == '\0') {
1119 return PR_ERROR_INT(cmd, PR_AUTH_BADPWD);
1120 }
1121
1122 if (strcmp(plaintext, ciphertext) == 0) {
1123 return PR_HANDLED(cmd);
1124 }
1125
1126 return PR_ERROR_INT(cmd, PR_AUTH_BADPWD);
1127 }
1128
sql_auth_empty(cmd_rec * cmd,const char * plaintext,const char * ciphertext)1129 static modret_t *sql_auth_empty(cmd_rec *cmd, const char *plaintext,
1130 const char *ciphertext) {
1131
1132 if (strcmp(ciphertext, "") == 0) {
1133 return PR_HANDLED(cmd);
1134 }
1135
1136 return PR_ERROR_INT(cmd, PR_AUTH_BADPWD);
1137 }
1138
1139 #if defined(HAVE_OPENSSL) || defined(PR_USE_OPENSSL)
sql_auth_openssl(cmd_rec * cmd,const char * plaintext,const char * ciphertext)1140 static modret_t *sql_auth_openssl(cmd_rec *cmd, const char *plaintext,
1141 const char *ciphertext) {
1142
1143 /* The ciphertext argument is a combined digest name and hashed value, of
1144 * the form "{digest}hash".
1145 */
1146
1147 EVP_MD_CTX *md_ctx;
1148 const EVP_MD *md;
1149
1150 /* According to RATS, the output buffer (buf) for EVP_EncodeBlock() needs to
1151 * be 4/3 the size of the input buffer (mdval). Let's make it easy, and
1152 * use an output buffer that's twice the size of the input buffer.
1153 */
1154 unsigned char buf[EVP_MAX_MD_SIZE*2], mdval[EVP_MAX_MD_SIZE];
1155 unsigned int mdlen;
1156
1157 char *digestname; /* ptr to name of the digest function */
1158 char *hashvalue; /* ptr to hashed value we're comparing to */
1159 char *copytext; /* temporary copy of the ciphertext string */
1160
1161 if (ciphertext[0] != '{') {
1162 sql_log(DEBUG_WARN, "%s", "no digest found in password hash");
1163 return PR_ERROR_INT(cmd, PR_AUTH_BADPWD);
1164 }
1165
1166 /* We need a copy of the ciphertext. */
1167 copytext = pstrdup(cmd->tmp_pool, ciphertext);
1168
1169 digestname = copytext + 1;
1170
1171 hashvalue = (char *) strchr(copytext, '}');
1172 if (hashvalue == NULL) {
1173 sql_log(DEBUG_WARN, "%s", "no terminating '}' for digest");
1174 return PR_ERROR_INT(cmd, PR_AUTH_BADPWD);
1175 }
1176
1177 *hashvalue = '\0';
1178 hashvalue++;
1179
1180 OpenSSL_add_all_digests();
1181
1182 md = EVP_get_digestbyname(digestname);
1183 if (md == NULL) {
1184 sql_log(DEBUG_WARN, "no such digest '%s' supported", digestname);
1185 return PR_ERROR_INT(cmd, PR_AUTH_BADPWD);
1186 }
1187
1188 md_ctx = EVP_MD_CTX_create();
1189 EVP_DigestInit(md_ctx, md);
1190 EVP_DigestUpdate(md_ctx, plaintext, strlen(plaintext));
1191 EVP_DigestFinal(md_ctx, mdval, &mdlen);
1192 EVP_MD_CTX_destroy(md_ctx);
1193
1194 memset(buf, '\0', sizeof(buf));
1195 EVP_EncodeBlock(buf, mdval, (int) mdlen);
1196
1197 if (strcmp((char *) buf, hashvalue) == 0) {
1198 return PR_HANDLED(cmd);
1199 }
1200
1201 return PR_ERROR_INT(cmd, PR_AUTH_BADPWD);
1202 }
1203 #endif
1204
1205 struct sql_authtype_handler {
1206 struct sql_authtype_handler *next, *prev;
1207 pool *pool;
1208 const char *name;
1209 modret_t *(*cb)(cmd_rec *, const char *, const char *);
1210 };
1211
1212 static struct sql_authtype_handler *sql_auth_list = NULL;
1213
sql_get_authtype(const char * name)1214 static struct sql_authtype_handler *sql_get_authtype(const char *name) {
1215 if (sql_auth_list) {
1216 struct sql_authtype_handler *sah;
1217
1218 for (sah = sql_auth_list; sah; sah = sah->next) {
1219 if (strcasecmp(sah->name, name) == 0) {
1220 return sah;
1221 }
1222 }
1223 }
1224
1225 errno = ENOENT;
1226 return NULL;
1227 }
1228
sql_register_authtype(const char * name,modret_t * (* cb)(cmd_rec *,const char *,const char *))1229 int sql_register_authtype(const char *name,
1230 modret_t *(*cb)(cmd_rec *, const char *, const char *)) {
1231 struct sql_authtype_handler *sah;
1232 pool *p;
1233
1234 if (name == NULL ||
1235 cb == NULL) {
1236 errno = EINVAL;
1237 return -1;
1238 }
1239
1240 /* Check for duplicates. */
1241 sah = sql_get_authtype(name);
1242 if (sah != NULL) {
1243 errno = EEXIST;
1244 return -1;
1245 }
1246
1247 if (sql_pool == NULL) {
1248 sql_pool = make_sub_pool(permanent_pool);
1249 pr_pool_tag(sql_pool, MOD_SQL_VERSION);
1250 }
1251
1252 p = pr_pool_create_sz(sql_pool, 128);
1253 sah = pcalloc(p, sizeof(struct sql_authtype_handler));
1254 sah->pool = p;
1255 sah->name = pstrdup(sah->pool, name);
1256 sah->cb = cb;
1257
1258 if (sql_auth_list) {
1259 sql_auth_list->prev = sah;
1260 sah->next = sql_auth_list;
1261 }
1262
1263 sql_auth_list = sah;
1264 return 0;
1265 }
1266
sql_unregister_authtype(const char * name)1267 int sql_unregister_authtype(const char *name) {
1268
1269 if (name == NULL) {
1270 errno = EINVAL;
1271 return -1;
1272 }
1273
1274 if (sql_auth_list) {
1275 struct sql_authtype_handler *sah;
1276
1277 for (sah = sql_auth_list; sah; sah = sah->next) {
1278 if (strcasecmp(sah->name, name) == 0) {
1279 if (sah->prev) {
1280 sah->prev->next = sah->next;
1281
1282 } else {
1283 /* This backend is the start of the list, so update the list
1284 * head pointer as well.
1285 */
1286 sql_auth_list = sah->next;
1287 }
1288
1289 if (sah->next) {
1290 sah->next->prev = sah->prev;
1291 }
1292
1293 destroy_pool(sah->pool);
1294 return 0;
1295 }
1296 }
1297 }
1298
1299 errno = ENOENT;
1300 return -1;
1301 }
1302
1303 /*****************************************************************
1304 *
1305 * INTERNAL HELPER FUNCTIONS
1306 *
1307 *****************************************************************/
1308
1309 /* find who core thinks is the user, and return a (backend-escaped)
1310 * version of that name */
_sql_realuser(cmd_rec * cmd)1311 static char *_sql_realuser(cmd_rec *cmd) {
1312 modret_t *mr = NULL;
1313 const char *user = NULL;
1314
1315 /* this is the userid given by the user */
1316 user = pr_table_get(session.notes, "mod_auth.orig-user", NULL);
1317
1318 /* Do we need to check for useralias? see mod_time.c, get_user_cmd_times(). */
1319 mr = sql_dispatch(sql_make_cmd(cmd->tmp_pool, 2, MOD_SQL_DEF_CONN_NAME,
1320 user), "sql_escapestring");
1321 if (check_response(mr, 0) < 0) {
1322 return NULL;
1323 }
1324
1325 return mr ? (char *) mr->data : NULL;
1326 }
1327
sql_define_conn(pool * p,const char * conn_name,const char * user,const char * passwd,const char * info,const char * ttl,const char * ssl_cert_file,const char * ssl_key_file,const char * ssl_ca_file,const char * ssl_ca_dir,const char * ssl_ciphers)1328 static int sql_define_conn(pool *p, const char *conn_name, const char *user,
1329 const char *passwd, const char *info, const char *ttl,
1330 const char *ssl_cert_file, const char *ssl_key_file,
1331 const char *ssl_ca_file, const char *ssl_ca_dir, const char *ssl_ciphers) {
1332 cmd_rec *cmd = NULL;
1333 modret_t *mr = NULL;
1334
1335 /* For backward compatibility of sub-modules' 'defineconn' handler, only
1336 * provide the SSL-related parameters if they are present.
1337 */
1338 if (ssl_cert_file != NULL ||
1339 ssl_key_file != NULL ||
1340 ssl_ca_file != NULL ||
1341 ssl_ca_dir != NULL ||
1342 ssl_ciphers != NULL) {
1343 cmd = sql_make_cmd(p, 10, conn_name, user, passwd, info, ttl, ssl_cert_file,
1344 ssl_key_file, ssl_ca_file, ssl_ca_dir, ssl_ciphers);
1345
1346 } else {
1347 cmd = sql_make_cmd(p, 5, conn_name, user, passwd, info, ttl);
1348 }
1349
1350 mr = sql_dispatch(cmd, "sql_defineconnection");
1351 if (check_response(mr, 0) < 0) {
1352 return -1;
1353 }
1354
1355 SQL_FREE_CMD(cmd);
1356
1357 if (pr_sql_conn_policy == SQL_CONN_POLICY_PERCONN) {
1358 /* Open a database connection now, so that we have a database connection
1359 * for the lifetime of the client's connection to the server.
1360 */
1361 cmd = sql_make_cmd(p, 1, conn_name);
1362 mr = sql_dispatch(cmd, "sql_open");
1363 if (check_response(mr, 0) < 0) {
1364 return -1;
1365 }
1366
1367 SQL_FREE_CMD(cmd);
1368 }
1369
1370 return 0;
1371 }
1372
sql_prepare_where(int flags,cmd_rec * cmd,int cnt,...)1373 static char *sql_prepare_where(int flags, cmd_rec *cmd, int cnt, ...) {
1374 int i, flag, nclauses = 0, res;
1375 char *buf = "", *where_clause;
1376 unsigned char *logfmt = NULL;
1377 size_t len = 0;
1378 va_list dummy;
1379 pool *tmp_pool;
1380 pr_jot_ctx_t *jot_ctx;
1381 pr_jot_parsed_t *jot_parsed;
1382 struct sql_resolved *resolved;
1383
1384 flag = 0;
1385 va_start(dummy, cnt);
1386 for (i = 0; i < cnt; i++) {
1387 char *clause = va_arg(dummy, char *);
1388 if (clause != NULL &&
1389 *clause != '\0') {
1390 nclauses++;
1391
1392 if (flag++) {
1393 buf = pstrcat(cmd->tmp_pool, buf, " AND ", NULL);
1394 }
1395 buf = pstrcat(cmd->tmp_pool, buf, "(", clause, ")", NULL);
1396 }
1397 }
1398 va_end(dummy);
1399
1400 if (nclauses == 0) {
1401 return NULL;
1402 }
1403
1404 if (flags & SQL_PREPARE_WHERE_FL_NO_TAGS) {
1405 /* Return the provided buffer as is; no processing needed. */
1406 return buf;
1407 }
1408
1409 /* In this function, we want to parse AND resolve the text in the same
1410 * step, IGNORING custom tags. The resolved text is what we return.
1411 */
1412
1413 tmp_pool = make_sub_pool(cmd->tmp_pool);
1414
1415 /* Allocate one byte more for the terminating NUL. */
1416 logfmt = pcalloc(tmp_pool, SQL_MAX_STMT_LEN + 1);
1417
1418 jot_ctx = pcalloc(tmp_pool, sizeof(pr_jot_ctx_t));
1419 jot_parsed = pcalloc(tmp_pool, sizeof(pr_jot_parsed_t));
1420 jot_parsed->bufsz = jot_parsed->buflen = SQL_MAX_STMT_LEN;
1421 jot_parsed->ptr = jot_parsed->buf = logfmt;
1422
1423 jot_ctx->log = jot_parsed;
1424
1425 /* Process variables in WHERE clauses, except any "%{num}" references. */
1426 res = pr_jot_parse_logfmt(tmp_pool, buf, jot_ctx, pr_jot_parse_on_meta,
1427 pr_jot_parse_on_unknown, pr_jot_parse_on_other, 0);
1428 if (res < 0) {
1429 sql_log(DEBUG_FUNC, "error parsing WHERE clause '%s': %s", buf,
1430 strerror(errno));
1431 destroy_pool(tmp_pool);
1432 return NULL;
1433 }
1434
1435 len = jot_parsed->bufsz - jot_parsed->buflen;
1436 logfmt[len] = '\0';
1437
1438 /* Allocate one byte more for the terminating NUL. */
1439 where_clause = pcalloc(cmd->tmp_pool, SQL_MAX_STMT_LEN + 1);
1440
1441 resolved = pcalloc(tmp_pool, sizeof(struct sql_resolved));
1442 resolved->bufsz = resolved->buflen = SQL_MAX_STMT_LEN;
1443 resolved->ptr = resolved->buf = where_clause;
1444 resolved->conn_name = MOD_SQL_DEF_CONN_NAME;
1445
1446 jot_ctx->log = resolved;
1447 jot_ctx->user_data = cmd;
1448
1449 res = pr_jot_resolve_logfmt(tmp_pool, cmd, NULL, logfmt, jot_ctx,
1450 sql_resolve_on_meta, sql_resolve_on_default, sql_resolve_on_other);
1451 if (res < 0) {
1452 sql_log(DEBUG_FUNC, "error resolving WHERE clause '%s': %s", buf,
1453 strerror(errno));
1454 destroy_pool(tmp_pool);
1455 return NULL;
1456 }
1457
1458 len = resolved->bufsz - resolved->buflen;
1459 where_clause[len] = '\0';
1460
1461 destroy_pool(tmp_pool);
1462
1463 pr_trace_msg(trace_channel, 19, "prepared WHERE clause '%s' as '%s'",
1464 buf, where_clause);
1465 return where_clause;
1466 }
1467
_sql_strcmp(const char * s1,const char * s2)1468 static int _sql_strcmp(const char *s1, const char *s2) {
1469 if ((s1 == NULL) || (s2 == NULL)) {
1470 return 1;
1471 }
1472
1473 return strcmp(s1, s2);
1474 }
1475
_group_gid(const void * val)1476 static unsigned int _group_gid(const void *val) {
1477 if (val == NULL) {
1478 return 0;
1479 }
1480
1481 return ((struct group *) val)->gr_gid;
1482 }
1483
_group_name(const void * val)1484 static unsigned int _group_name(const void *val) {
1485 register unsigned int i;
1486 size_t namelen;
1487 char *name;
1488 unsigned int nameval = 0;
1489
1490 if (val == NULL) {
1491 return 0;
1492 }
1493
1494 name = ((struct group *) val)->gr_name;
1495 if (name == NULL) {
1496 return 0;
1497 }
1498
1499 namelen = strlen(name);
1500 for (i = 0; i < namelen; i++) {
1501 nameval += name[i];
1502 }
1503
1504 return nameval;
1505 }
1506
_groupcmp(const void * val1,const void * val2)1507 static int _groupcmp(const void *val1, const void *val2) {
1508 if ((val1 == NULL) || (val2 == NULL)) {
1509 return 0;
1510 }
1511
1512 /* either the groupnames match or the GIDs match */
1513
1514 if (_sql_strcmp(((struct group *) val1)->gr_name,
1515 ((struct group *) val2)->gr_name) == 0) {
1516 return 1;
1517 }
1518
1519 if (((struct group *) val1)->gr_gid == ((struct group *) val2)->gr_gid) {
1520 return 1;
1521 }
1522
1523 return 0;
1524 }
1525
_passwd_uid(const void * val)1526 static unsigned int _passwd_uid(const void *val) {
1527 if (val == NULL) {
1528 return 0;
1529 }
1530
1531 return ((struct passwd *) val)->pw_uid;
1532 }
1533
_passwd_name(const void * val)1534 static unsigned int _passwd_name(const void *val) {
1535 register unsigned int i;
1536 char *name;
1537 size_t namelen;
1538 unsigned int nameval = 0;
1539
1540 if (val == NULL) {
1541 return 0;
1542 }
1543
1544 name = ((struct passwd *) val)->pw_name;
1545 if (name == NULL) {
1546 return 0;
1547 }
1548
1549 namelen = strlen(name);
1550 for (i = 0; i < namelen; i++) {
1551 nameval += name[i];
1552 }
1553
1554 return nameval;
1555 }
1556
_passwdcmp(const void * val1,const void * val2)1557 static int _passwdcmp(const void *val1, const void *val2) {
1558 if ((val1 == NULL) || (val2 == NULL)) {
1559 return 0;
1560 }
1561
1562 /* either the usernames match or the UIDs match */
1563 if (_sql_strcmp(((struct passwd *) val1)->pw_name,
1564 ((struct passwd *) val2)->pw_name) == 0) {
1565 return 1;
1566 }
1567
1568 if (((struct passwd *) val1)->pw_uid == ((struct passwd *) val2)->pw_uid) {
1569 return 1;
1570 }
1571
1572 return 0;
1573 }
1574
show_group(pool * p,struct group * g)1575 static void show_group(pool *p, struct group *g) {
1576 char *members = "";
1577
1578 if (g == NULL) {
1579 sql_log(DEBUG_INFO, "%s", "NULL group to show_group()");
1580 return;
1581 }
1582
1583 if (g->gr_mem != NULL) {
1584 char **member;
1585
1586 member = g->gr_mem;
1587
1588 while (*member != NULL) {
1589 pr_signals_handle();
1590
1591 members = pstrcat(p, members, *members ? ", " : "", *member, NULL);
1592 member++;
1593 }
1594 }
1595
1596 sql_log(DEBUG_INFO, "+ grp.gr_name : %s", g->gr_name);
1597 sql_log(DEBUG_INFO, "+ grp.gr_gid : %s", pr_gid2str(NULL, g->gr_gid));
1598 sql_log(DEBUG_INFO, "+ grp.gr_mem : %s", members);
1599
1600 return;
1601 }
1602
show_passwd(struct passwd * p)1603 static void show_passwd(struct passwd *p) {
1604 if (p == NULL) {
1605 sql_log(DEBUG_INFO, "%s", "NULL passwd to show_passwd()");
1606 return;
1607 }
1608
1609 sql_log(DEBUG_INFO, "+ pwd.pw_name : %s", p->pw_name);
1610 sql_log(DEBUG_INFO, "+ pwd.pw_uid : %s", pr_uid2str(NULL, p->pw_uid));
1611 sql_log(DEBUG_INFO, "+ pwd.pw_gid : %s", pr_gid2str(NULL, p->pw_gid));
1612 sql_log(DEBUG_INFO, "+ pwd.pw_dir : %s", p->pw_dir ?
1613 p->pw_dir : "(null)");
1614 sql_log(DEBUG_INFO, "+ pwd.pw_shell : %s", p->pw_shell ?
1615 p->pw_shell : "(null)");
1616
1617 return;
1618 }
1619
1620 /* _sql_addpasswd: creates a passwd and adds it to the passwd struct
1621 * cache if it doesn't already exist. Returns the created passwd
1622 * struct, or the pre-existing struct if there was one.
1623 *
1624 * DOES NOT CHECK ARGUMENTS. CALLING FUNCTIONS NEED TO MAKE SURE
1625 * THEY PASS VALID DATA
1626 */
_sql_addpasswd(cmd_rec * cmd,char * username,char * password,uid_t uid,gid_t gid,char * shell,char * dir)1627 static struct passwd *_sql_addpasswd(cmd_rec *cmd, char *username,
1628 char *password, uid_t uid, gid_t gid, char *shell, char *dir) {
1629 struct passwd *cached = NULL;
1630 struct passwd *pwd = NULL;
1631
1632 pwd = pcalloc(cmd->tmp_pool, sizeof(struct passwd));
1633 pwd->pw_uid = uid;
1634 pwd->pw_name = username;
1635
1636 /* check to make sure the entry doesn't exist in the cache */
1637 cached = (struct passwd *) cache_findvalue(passwd_name_cache, pwd);
1638 if (cached != NULL) {
1639 pwd = cached;
1640 sql_log(DEBUG_INFO, "cache hit for user '%s'", pwd->pw_name);
1641
1642 } else {
1643 pwd = pcalloc(sql_pool, sizeof(struct passwd));
1644
1645 if (username)
1646 pwd->pw_name = pstrdup(sql_pool, username);
1647
1648 if (password)
1649 pwd->pw_passwd = pstrdup(sql_pool, password);
1650
1651 pwd->pw_uid = uid;
1652 pwd->pw_gid = gid;
1653
1654 if (shell) {
1655 pwd->pw_shell = pstrdup(sql_pool, shell);
1656
1657 if (pr_table_add(session.notes, "shell", pwd->pw_shell, 0) < 0) {
1658 int xerrno = errno;
1659
1660 if (xerrno != EEXIST) {
1661 pr_trace_msg(trace_channel, 8,
1662 "error setting 'shell' session note: %s", strerror(xerrno));
1663 }
1664 }
1665 }
1666
1667 if (dir) {
1668 pwd->pw_dir = pstrdup(sql_pool, dir);
1669
1670 if (pr_table_add(session.notes, "home", pwd->pw_dir, 0) < 0) {
1671 int xerrno = errno;
1672
1673 if (xerrno != EEXIST) {
1674 pr_trace_msg(trace_channel, 8,
1675 "error setting 'home' session note: %s", strerror(xerrno));
1676 }
1677 }
1678 }
1679
1680 cache_addentry(passwd_name_cache, pwd);
1681 cache_addentry(passwd_uid_cache, pwd);
1682
1683 sql_log(DEBUG_INFO, "cache miss for user '%s'", pwd->pw_name);
1684 sql_log(DEBUG_INFO, "user '%s' cached", pwd->pw_name);
1685 show_passwd(pwd);
1686 }
1687
1688 return pwd;
1689 }
1690
sql_getuserprimarykey(cmd_rec * cmd,const char * username)1691 static int sql_getuserprimarykey(cmd_rec *cmd, const char *username) {
1692 sql_data_t *sd = NULL;
1693 modret_t *mr = NULL;
1694 char *key_field = NULL, *key_value = NULL, *ptr = NULL;
1695 config_rec *c;
1696 const void *v = NULL;
1697
1698 v = pr_table_get(session.notes, "sql.user-primary-key", NULL);
1699 if (v != NULL) {
1700 /* Already have UserPrimaryKey. */
1701 return 0;
1702 }
1703
1704 c = find_config(main_server->conf, CONF_PARAM, "SQLUserPrimaryKey", FALSE);
1705 if (c == NULL) {
1706 return 0;
1707 }
1708
1709 key_field = c->argv[0];
1710 if (strncmp(key_field, "custom:/", 8) == 0) {
1711 config_rec *custom_c = NULL;
1712 char *named_query;
1713
1714 ptr = key_field + 8;
1715 named_query = pstrcat(cmd->tmp_pool, "SQLNamedQuery_", ptr, NULL);
1716
1717 custom_c = find_config(main_server->conf, CONF_PARAM, named_query, FALSE);
1718 if (custom_c == NULL) {
1719 sql_log(DEBUG_INFO, "error: unable to resolve custom "
1720 "SQLNamedQuery name '%s'", (char *) ptr);
1721 ptr = NULL;
1722 }
1723 }
1724
1725 if (ptr == NULL) {
1726 char *where;
1727
1728 where = pstrcat(cmd->tmp_pool, cmap.usrfield, " = '", username, "'", NULL);
1729
1730 mr = sql_dispatch(sql_make_cmd(cmd->tmp_pool, 5, MOD_SQL_DEF_CONN_NAME,
1731 cmap.usrtable, key_field, where, "1"), "sql_select");
1732 if (check_response(mr, 0) < 0) {
1733 return -1;
1734 }
1735
1736 if (MODRET_HASDATA(mr)) {
1737 sd = (sql_data_t *) mr->data;
1738 }
1739
1740 } else {
1741 mr = sql_lookup(sql_make_cmd(cmd->tmp_pool, 3, MOD_SQL_DEF_CONN_NAME, ptr,
1742 username));
1743 if (check_response(mr, 0) < 0) {
1744 return -1;
1745 }
1746
1747 if (MODRET_HASDATA(mr)) {
1748 array_header *ah = (array_header *) mr->data;
1749 sd = pcalloc(cmd->tmp_pool, sizeof(sql_data_t));
1750
1751 /* Assume the query only returned 1 row. */
1752 sd->fnum = ah->nelts;
1753
1754 sql_log(DEBUG_INFO,
1755 "custom SQLUserPrimaryKey query '%s' returned %lu columns for "
1756 "user '%s'", ptr, sd->fnum, username);
1757 if (sd->fnum) {
1758 sd->rnum = 1;
1759 sd->data = (char **) ah->elts;
1760
1761 } else {
1762 sd->rnum = 0;
1763 sd->data = NULL;
1764 }
1765 }
1766 }
1767
1768 /* If we have no data...*/
1769 if (sd == NULL ||
1770 sd->rnum == 0) {
1771 errno = ENOENT;
1772 return -1;
1773 }
1774
1775 key_value = sd->data[0];
1776 if (pr_table_add(session.notes, "sql.user-primary-key",
1777 pstrdup(session.pool, key_value), 0) < 0) {
1778 sql_log(DEBUG_INFO,
1779 "error stashing 'sql.user-primary-key' note for value '%s': %s",
1780 key_value, strerror(errno));
1781 }
1782
1783 return 0;
1784 }
1785
sql_getgroupprimarykey(cmd_rec * cmd,const char * groupname)1786 static int sql_getgroupprimarykey(cmd_rec *cmd, const char *groupname) {
1787 sql_data_t *sd = NULL;
1788 modret_t *mr = NULL;
1789 char *key_field = NULL, *key_value = NULL, *ptr = NULL;
1790 config_rec *c;
1791 const void *v = NULL;
1792
1793 v = pr_table_get(session.notes, "sql.group-primary-key", NULL);
1794 if (v != NULL) {
1795 /* Already have GroupPrimaryKey. */
1796 return 0;
1797 }
1798
1799 c = find_config(main_server->conf, CONF_PARAM, "SQLGroupPrimaryKey", FALSE);
1800 if (c == NULL) {
1801 return 0;
1802 }
1803
1804 key_field = c->argv[0];
1805 if (strncmp(key_field, "custom:/", 8) == 0) {
1806 config_rec *custom_c = NULL;
1807 char *named_query;
1808
1809 ptr = key_field + 8;
1810 named_query = pstrcat(cmd->tmp_pool, "SQLNamedQuery_", ptr, NULL);
1811
1812 custom_c = find_config(main_server->conf, CONF_PARAM, named_query, FALSE);
1813 if (custom_c == NULL) {
1814 sql_log(DEBUG_INFO, "error: unable to resolve custom "
1815 "SQLNamedQuery name '%s'", (char *) ptr);
1816 ptr = NULL;
1817 }
1818 }
1819
1820 if (ptr == NULL) {
1821 char *where;
1822
1823 where = pstrcat(cmd->tmp_pool, cmap.grpfield, " = '", groupname, "'", NULL);
1824
1825 mr = sql_dispatch(sql_make_cmd(cmd->tmp_pool, 5, MOD_SQL_DEF_CONN_NAME,
1826 cmap.grptable, key_field, where, "1"), "sql_select");
1827 if (check_response(mr, 0) < 0) {
1828 return -1;
1829 }
1830
1831 if (MODRET_HASDATA(mr)) {
1832 sd = (sql_data_t *) mr->data;
1833 }
1834
1835 } else {
1836 mr = sql_lookup(sql_make_cmd(cmd->tmp_pool, 3, MOD_SQL_DEF_CONN_NAME, ptr,
1837 groupname));
1838 if (check_response(mr, 0) < 0) {
1839 return -1;
1840 }
1841
1842 if (MODRET_HASDATA(mr)) {
1843 array_header *ah = (array_header *) mr->data;
1844 sd = pcalloc(cmd->tmp_pool, sizeof(sql_data_t));
1845
1846 /* Assume the query only returned 1 row. */
1847 sd->fnum = ah->nelts;
1848
1849 sql_log(DEBUG_INFO,
1850 "custom SQLGroupPrimaryKey query '%s' returned %lu columns for "
1851 "group '%s'", ptr, sd->fnum, groupname);
1852 if (sd->fnum) {
1853 sd->rnum = 1;
1854 sd->data = (char **) ah->elts;
1855
1856 } else {
1857 sd->rnum = 0;
1858 sd->data = NULL;
1859 }
1860 }
1861 }
1862
1863 /* If we have no data...*/
1864 if (sd == NULL ||
1865 sd->rnum == 0) {
1866 errno = ENOENT;
1867 return -1;
1868 }
1869
1870 key_value = sd->data[0];
1871 if (pr_table_add(session.notes, "sql.group-primary-key",
1872 pstrdup(session.pool, key_value), 0) < 0) {
1873 sql_log(DEBUG_INFO,
1874 "error stashing 'sql.group-primary-key' note for value '%s': %s",
1875 key_value, strerror(errno));
1876 }
1877
1878 return 0;
1879 }
1880
sql_getpasswd(cmd_rec * cmd,struct passwd * p)1881 static struct passwd *sql_getpasswd(cmd_rec *cmd, struct passwd *p) {
1882 sql_data_t *sd = NULL;
1883 modret_t *mr = NULL;
1884 struct passwd *pwd = NULL;
1885 char *usrwhere, *where;
1886 char *realname = NULL;
1887 int i = 0;
1888
1889 char *username = NULL;
1890 char *password = NULL;
1891 char *shell = NULL;
1892 char *dir = NULL;
1893 uid_t uid = 0;
1894 gid_t gid = 0;
1895
1896 if (p == NULL) {
1897 sql_log(DEBUG_WARN, "%s", "sql_getpasswd called with NULL passwd struct");
1898 sql_log(DEBUG_WARN, "%s", "THIS SHOULD NEVER HAPPEN");
1899 return NULL;
1900 }
1901
1902 /* Check to see if the passwd already exists in one of the passwd caches.
1903 * Give preference to name-based lookups, as opposed to UID-based lookups.
1904 */
1905 if (p->pw_name != NULL) {
1906 pwd = (struct passwd *) cache_findvalue(passwd_name_cache, p);
1907
1908 } else {
1909 pwd = (struct passwd *) cache_findvalue(passwd_uid_cache, p);
1910 }
1911
1912 if (pwd != NULL) {
1913 sql_log(DEBUG_AUTH, "cache hit for user '%s'", pwd->pw_name);
1914
1915 /* Check for negatively cached passwds, which will have NULL
1916 * passwd/home/shell.
1917 */
1918 if (pwd->pw_passwd == NULL &&
1919 pwd->pw_shell == NULL &&
1920 pwd->pw_dir == NULL) {
1921 sql_log(DEBUG_AUTH, "negative cache entry for user '%s'", pwd->pw_name);
1922 return NULL;
1923 }
1924
1925 return pwd;
1926 }
1927
1928 if (p->pw_name != NULL) {
1929 realname = p->pw_name;
1930
1931 mr = sql_dispatch(sql_make_cmd(cmd->tmp_pool, 2, MOD_SQL_DEF_CONN_NAME,
1932 realname), "sql_escapestring");
1933 if (check_response(mr, 0) < 0) {
1934 return NULL;
1935 }
1936
1937 username = (char *) mr->data;
1938 usrwhere = pstrcat(cmd->tmp_pool, cmap.usrfield, "='", username, "'", NULL);
1939
1940 sql_log(DEBUG_WARN, "cache miss for user '%s'", realname);
1941
1942 if (!cmap.usercustom) {
1943 /* The following nested function calls may look a little strange, but
1944 * it is deliberate. We want to handle any tags/variables within the
1945 * cmap.userwhere string (i.e. the SQLUserWhereClause directive, if
1946 * configured), but we do NOT want to handle any tags/variables in
1947 * the usrwhere variable (a string we concatenated ourselves). The
1948 * usrwhere variable contains the user name, and we need to handle that
1949 * string as-is, lest we corrupt/change the user name.
1950 */
1951
1952 where = sql_prepare_where(SQL_PREPARE_WHERE_FL_NO_TAGS, cmd, 2, usrwhere,
1953 sql_prepare_where(0, cmd, 1, cmap.userwhere, NULL), NULL);
1954
1955 mr = sql_dispatch(sql_make_cmd(cmd->tmp_pool, 5, MOD_SQL_DEF_CONN_NAME,
1956 cmap.usrtable, cmap.usrfields, where, "1"), "sql_select");
1957 if (check_response(mr, 0) < 0) {
1958 return NULL;
1959 }
1960
1961 if (MODRET_HASDATA(mr)) {
1962 sd = (sql_data_t *) mr->data;
1963 }
1964
1965 } else {
1966 mr = sql_lookup(sql_make_cmd(cmd->tmp_pool, 3, MOD_SQL_DEF_CONN_NAME,
1967 cmap.usercustom, realname ? realname : "NULL"));
1968
1969 if (check_response(mr, 0) < 0) {
1970 return NULL;
1971 }
1972
1973 if (MODRET_HASDATA(mr)) {
1974 array_header *ah = (array_header *) mr->data;
1975 sd = pcalloc(cmd->tmp_pool, sizeof(sql_data_t));
1976
1977 /* Assume the query only returned 1 row. */
1978 sd->fnum = ah->nelts;
1979
1980 sql_log(DEBUG_INFO,
1981 "custom SQLUserInfo query '%s' returned %lu columns for user '%s'",
1982 cmap.usercustom, sd->fnum, realname);
1983 if (sd->fnum) {
1984 sd->rnum = 1;
1985 sd->data = (char **) ah->elts;
1986
1987 } else {
1988 sd->rnum = 0;
1989 sd->data = NULL;
1990 }
1991 }
1992 }
1993
1994 } else {
1995 /* Assume we have a UID */
1996 const char *uidstr;
1997
1998 uidstr = pr_uid2str(cmd->tmp_pool, p->pw_uid);
1999 sql_log(DEBUG_WARN, "cache miss for UID '%s'", uidstr);
2000
2001 if (!cmap.usercustombyid) {
2002 if (cmap.uidfield) {
2003 usrwhere = pstrcat(cmd->tmp_pool, cmap.uidfield, " = ", uidstr, NULL);
2004
2005 where = sql_prepare_where(SQL_PREPARE_WHERE_FL_NO_TAGS, cmd, 2,
2006 usrwhere, sql_prepare_where(0, cmd, 1, cmap.userwhere, NULL), NULL);
2007
2008 mr = sql_dispatch(sql_make_cmd(cmd->tmp_pool, 5,
2009 MOD_SQL_DEF_CONN_NAME, cmap.usrtable, cmap.usrfields, where, "1"),
2010 "sql_select");
2011 if (check_response(mr, 0) < 0) {
2012 return NULL;
2013 }
2014
2015 if (MODRET_HASDATA(mr)) {
2016 sd = (sql_data_t *) mr->data;
2017 }
2018
2019 } else {
2020 sql_log(DEBUG_WARN, "no user UID field configured, declining to "
2021 "lookup UID '%s'", uidstr);
2022
2023 /* If no UID field has been configured, return now and let other
2024 * modules possibly have a chance at resolving this UID to a name.
2025 */
2026 return NULL;
2027 }
2028
2029 } else {
2030 array_header *ah = NULL;
2031
2032 mr = sql_lookup(sql_make_cmd(cmd->tmp_pool, 3, MOD_SQL_DEF_CONN_NAME,
2033 cmap.usercustombyid, uidstr));
2034 if (check_response(mr, 0) < 0) {
2035 return NULL;
2036 }
2037
2038 ah = mr->data;
2039
2040 sd = pcalloc(cmd->tmp_pool, sizeof(sql_data_t));
2041
2042 /* Assume the query only return 1 row. */
2043 sd->fnum = ah->nelts;
2044 if (sd->fnum) {
2045 sd->rnum = 1;
2046 sd->data = (char **) ah->elts;
2047
2048 } else {
2049 sd->rnum = 0;
2050 sd->data = NULL;
2051 }
2052 }
2053 }
2054
2055 /* if we have no data.. */
2056 if (sd == NULL ||
2057 sd->rnum == 0) {
2058 if (!cmap.negative_cache) {
2059 return NULL;
2060
2061 } else {
2062 /* If doing caching of negative lookups, cache this failed lookup.
2063 * Use the default UID and GID.
2064 */
2065 return _sql_addpasswd(cmd, username, NULL, p->pw_uid, p->pw_gid,
2066 NULL, NULL);
2067 }
2068 }
2069
2070 i = 0;
2071
2072 username = sd->data[i++];
2073 password = sd->data[i++];
2074
2075 uid = cmap.defaultuid;
2076 if (cmap.uidfield) {
2077 if (sd->data[i]) {
2078 if (pr_str2uid(sd->data[i++], &uid) < 0) {
2079 uid = cmap.defaultuid;
2080 }
2081
2082 } else {
2083 i++;
2084 }
2085 }
2086
2087 gid = cmap.defaultgid;
2088 if (cmap.gidfield) {
2089 if (sd->data[i]) {
2090 if (pr_str2gid(sd->data[i++], &gid) < 0) {
2091 gid = cmap.defaultgid;
2092 }
2093
2094 } else {
2095 i++;
2096 }
2097 }
2098
2099 dir = cmap.defaulthomedir;
2100 if (sd->data[i]) {
2101 if (strcmp(sd->data[i], "") == 0 ||
2102 strcmp(sd->data[i], "NULL") == 0) {
2103
2104 /* Leave dir pointing to the SQLDefaultHomedir, if any. */
2105 i++;
2106
2107 } else {
2108 dir = sd->data[i++];
2109 }
2110 }
2111
2112 if (cmap.shellfield) {
2113 if (sd->fnum-1 < (unsigned long) i ||
2114 !sd->data[i]) {
2115
2116 /* Make sure that, if configured, the shell value is valid, and scream
2117 * if it is not.
2118 */
2119 sql_log(DEBUG_WARN, "NULL shell column value");
2120 shell = NULL;
2121
2122 } else {
2123 shell = sd->data[i];
2124 }
2125
2126 } else {
2127 shell = NULL;
2128 }
2129
2130 if (uid < cmap.minuseruid) {
2131 sql_log(DEBUG_INFO, "user UID %s below SQLMinUserUID %s, using "
2132 "SQLDefaultUID %s", pr_uid2str(cmd->tmp_pool, uid),
2133 pr_uid2str(cmd->tmp_pool, cmap.minuseruid),
2134 pr_uid2str(cmd->tmp_pool, cmap.defaultuid));
2135 uid = cmap.defaultuid;
2136 }
2137
2138 if (gid < cmap.minusergid) {
2139 sql_log(DEBUG_INFO, "user GID %s below SQLMinUserGID %s, using "
2140 "SQLDefaultGID %s", pr_gid2str(cmd->tmp_pool, gid),
2141 pr_gid2str(cmd->tmp_pool, cmap.minusergid),
2142 pr_gid2str(cmd->tmp_pool, cmap.defaultgid));
2143 gid = cmap.defaultgid;
2144 }
2145
2146 return _sql_addpasswd(cmd, username, password, uid, gid, shell, dir);
2147 }
2148
2149 /* _sql_addgroup: creates a group and adds it to the group struct
2150 * cache if it doesn't already exist. Returns the created group
2151 * struct, or the pre-existing struct if there was one.
2152 *
2153 * DOES NOT CHECK ARGUMENTS. CALLING FUNCTIONS NEED TO MAKE SURE
2154 * THEY PASS VALID DATA
2155 */
_sql_addgroup(cmd_rec * cmd,char * groupname,gid_t gid,array_header * ah)2156 static struct group *_sql_addgroup(cmd_rec *cmd, char *groupname, gid_t gid,
2157 array_header *ah) {
2158 struct group *cached = NULL;
2159 struct group *grp = NULL;
2160
2161 grp = pcalloc(cmd->tmp_pool, sizeof(struct group));
2162 grp->gr_gid = gid;
2163 grp->gr_name = groupname;
2164
2165 /* check to make sure the entry doesn't exist in the cache */
2166 if ((cached = (struct group *) cache_findvalue(group_name_cache, grp)) != NULL) {
2167 grp = cached;
2168 sql_log(DEBUG_INFO, "cache hit for group '%s'", grp->gr_name);
2169
2170 } else {
2171 grp = pcalloc(sql_pool, sizeof(struct group));
2172
2173 if (groupname) {
2174 grp->gr_name = pstrdup(sql_pool, groupname);
2175
2176 if (pr_table_add(session.notes, "primary-group", grp->gr_name, 0) < 0) {
2177 int xerrno = errno;
2178
2179 if (xerrno != EEXIST) {
2180 pr_trace_msg(trace_channel, 8,
2181 "error setting 'primary-group' session note: %s", strerror(xerrno));
2182 }
2183 }
2184 }
2185
2186 grp->gr_gid = gid;
2187
2188 if (ah) {
2189 register unsigned int i;
2190
2191 /* finish filling in the group */
2192 grp->gr_mem = (char **) pcalloc(sql_pool,
2193 sizeof(char *) * (ah->nelts + 1));
2194
2195 for (i = 0; i < ah->nelts; i++) {
2196 grp->gr_mem[i] = pstrdup(sql_pool, ((char **) ah->elts)[i]);
2197 }
2198
2199 grp->gr_mem[i] = NULL;
2200 }
2201
2202 cache_addentry(group_name_cache, grp);
2203 cache_addentry(group_gid_cache, grp);
2204
2205 sql_log(DEBUG_INFO, "cache miss for group '%s'", grp->gr_name);
2206 sql_log(DEBUG_INFO, "group '%s' cached", grp->gr_name);
2207 show_group(cmd->tmp_pool, grp);
2208 }
2209
2210 return grp;
2211 }
2212
sql_getgroup(cmd_rec * cmd,struct group * g)2213 static struct group *sql_getgroup(cmd_rec *cmd, struct group *g) {
2214 struct group *grp = NULL;
2215 modret_t *mr = NULL;
2216 int cnt = 0;
2217 sql_data_t *sd = NULL;
2218 char *groupname = NULL;
2219 char **rows = NULL;
2220 int numrows = 0;
2221 array_header *ah = NULL;
2222 char *members = NULL;
2223 char *member = NULL;
2224 char *grpwhere;
2225 char *where;
2226 char *iterator;
2227
2228 gid_t gid = 0;
2229
2230 if (g == NULL) {
2231 sql_log(DEBUG_WARN, "%s", "sql_getgroup called with NULL group struct");
2232 sql_log(DEBUG_WARN, "%s", "THIS SHOULD NEVER HAPPEN");
2233 return NULL;
2234 }
2235
2236 /* check to see if the group already exists in one of the group caches */
2237 if (((grp = (struct group *) cache_findvalue(group_name_cache, g)) != NULL) ||
2238 ((grp = (struct group *) cache_findvalue(group_gid_cache, g)) != NULL)) {
2239 sql_log(DEBUG_AUTH, "cache hit for group '%s'", grp->gr_name);
2240
2241 /* Check for negatively cached groups, which will have NULL gr_mem. */
2242 if (!grp->gr_mem) {
2243 sql_log(DEBUG_AUTH, "negative cache entry for group '%s'", grp->gr_name);
2244 return NULL;
2245 }
2246
2247 return grp;
2248 }
2249
2250 if (g->gr_name != NULL) {
2251 groupname = g->gr_name;
2252 sql_log(DEBUG_WARN, "cache miss for group '%s'", groupname);
2253
2254 } else {
2255 const char *gidstr = NULL;
2256
2257 /* Get groupname from GID */
2258 gidstr = pr_gid2str(NULL, g->gr_gid);
2259
2260 sql_log(DEBUG_WARN, "cache miss for GID '%s'", gidstr);
2261
2262 if (!cmap.groupcustombyid) {
2263 if (cmap.grpgidfield) {
2264 grpwhere = pstrcat(cmd->tmp_pool, cmap.grpgidfield, " = ", gidstr,
2265 NULL);
2266
2267 } else {
2268 sql_log(DEBUG_WARN, "no group GID field configured, declining to "
2269 "lookup GID '%s'", gidstr);
2270
2271 /* If no GID field has been configured, return now and let other
2272 * modules possibly have a chance at resolving this GID to a name.
2273 */
2274 return NULL;
2275 }
2276
2277 where = sql_prepare_where(SQL_PREPARE_WHERE_FL_NO_TAGS, cmd, 2, grpwhere,
2278 sql_prepare_where(0, cmd, 1, cmap.groupwhere, NULL), NULL);
2279
2280 mr = sql_dispatch(sql_make_cmd(cmd->tmp_pool, 5, MOD_SQL_DEF_CONN_NAME,
2281 cmap.grptable, cmap.grpfield, where, "1"), "sql_select");
2282 if (check_response(mr, 0) < 0) {
2283 return NULL;
2284 }
2285
2286 sd = (sql_data_t *) mr->data;
2287
2288 } else {
2289 mr = sql_lookup(sql_make_cmd(cmd->tmp_pool, 3, MOD_SQL_DEF_CONN_NAME,
2290 cmap.groupcustombyid, gidstr));
2291 if (check_response(mr, 0) < 0) {
2292 return NULL;
2293 }
2294
2295 ah = mr->data;
2296
2297 sd = pcalloc(cmd->tmp_pool, sizeof(sql_data_t));
2298
2299 /* Assume the query only return 1 row. */
2300 sd->fnum = ah->nelts;
2301 if (sd->fnum) {
2302 sd->rnum = 1;
2303 sd->data = (char **) ah->elts;
2304
2305 } else {
2306 sd->rnum = 0;
2307 sd->data = NULL;
2308 }
2309 }
2310
2311 /* If we have no data.. */
2312 if (sd->rnum == 0)
2313 return NULL;
2314
2315 groupname = sd->data[0];
2316 }
2317
2318 if (!cmap.groupcustombyname) {
2319 grpwhere = pstrcat(cmd->tmp_pool, cmap.grpfield, " = '", groupname, "'",
2320 NULL);
2321
2322 where = sql_prepare_where(SQL_PREPARE_WHERE_FL_NO_TAGS, cmd, 2, grpwhere,
2323 sql_prepare_where(0, cmd, 1, cmap.groupwhere, NULL), NULL);
2324
2325 mr = sql_dispatch(sql_make_cmd(cmd->tmp_pool, 4, MOD_SQL_DEF_CONN_NAME,
2326 cmap.grptable, cmap.grpfields, where), "sql_select");
2327 if (check_response(mr, 0) < 0) {
2328 return NULL;
2329 }
2330
2331 sd = (sql_data_t *) mr->data;
2332
2333 } else {
2334 mr = sql_lookup(sql_make_cmd(cmd->tmp_pool, 3, MOD_SQL_DEF_CONN_NAME,
2335 cmap.groupcustombyname, groupname ? groupname : "NULL"));
2336 if (check_response(mr, 0) < 0) {
2337 return NULL;
2338 }
2339
2340 ah = mr->data;
2341 sd = pcalloc(cmd->tmp_pool, sizeof(sql_data_t));
2342
2343 /* Assume the query only returned 1 row. */
2344 sd->fnum = ah->nelts;
2345
2346 if (sd->fnum) {
2347 sd->rnum = 1;
2348 sd->data = (char **) ah->elts;
2349
2350 } else {
2351 sd->rnum = 0;
2352 sd->data = NULL;
2353 }
2354 }
2355
2356 /* if we have no data.. */
2357 if (sd->rnum == 0) {
2358 if (!cmap.negative_cache) {
2359 return NULL;
2360
2361 } else {
2362
2363 /* If doing caching of negative lookups, cache this failed lookup. */
2364 return _sql_addgroup(cmd, groupname, g->gr_gid, NULL);
2365 }
2366 }
2367
2368 rows = sd->data;
2369 numrows = sd->rnum;
2370
2371 gid = (gid_t) strtoul(rows[1], NULL, 10);
2372
2373 /* Painful.. we need to walk through the returned rows and fill in our
2374 * members. Every third element in a row is a member field, and every
2375 * member field can have multiple members.
2376 */
2377
2378 ah = make_array(cmd->tmp_pool, 10, sizeof(char *));
2379
2380 for (cnt = 0; cnt < numrows; cnt++) {
2381 members = rows[(cnt * 3) + 2];
2382 iterator = members;
2383
2384 pr_signals_handle();
2385
2386 /* If the row is null, continue.. */
2387 if (members == NULL)
2388 continue;
2389
2390 /* For each member in the list, toss 'em into the array. no
2391 * need to copy the string -- _sql_addgroup will do it for us
2392 */
2393 for (member = strsep(&iterator, ","); member;
2394 member = strsep(&iterator, ",")) {
2395 if (*member == '\0') {
2396 continue;
2397 }
2398
2399 *((char **) push_array(ah)) = member;
2400 }
2401 }
2402
2403 return _sql_addgroup(cmd, groupname, gid, ah);
2404 }
2405
_setstats(cmd_rec * cmd,int fstor,int fretr,int bstor,int bretr)2406 static void _setstats(cmd_rec *cmd, int fstor, int fretr, int bstor,
2407 int bretr) {
2408 /*
2409 * if anyone has a better way of doing this, let me know..
2410 */
2411 char query[256] = { '\0' };
2412 char *usrwhere, *where;
2413 modret_t *mr = NULL;
2414
2415 pr_snprintf(query, sizeof(query),
2416 "%s = %s + %i, %s = %s + %i, %s = %s + %i, %s = %s + %i",
2417 cmap.sql_fstor, cmap.sql_fstor, fstor,
2418 cmap.sql_fretr, cmap.sql_fretr, fretr,
2419 cmap.sql_bstor, cmap.sql_bstor, bstor,
2420 cmap.sql_bretr, cmap.sql_bretr, bretr);
2421
2422 usrwhere = pstrcat(cmd->tmp_pool, cmap.usrfield, " = '", _sql_realuser(cmd),
2423 "'", NULL);
2424
2425 where = sql_prepare_where(SQL_PREPARE_WHERE_FL_NO_TAGS, cmd, 2, usrwhere,
2426 sql_prepare_where(0, cmd, 1, cmap.userwhere, NULL), NULL);
2427
2428 mr = sql_dispatch(sql_make_cmd(cmd->tmp_pool, 4, MOD_SQL_DEF_CONN_NAME,
2429 cmap.usrtable, query, where), "sql_update");
2430 (void) check_response(mr, 0);
2431 }
2432
sql_getgroups(cmd_rec * cmd)2433 static int sql_getgroups(cmd_rec *cmd) {
2434 struct passwd *pw = NULL, lpw;
2435 struct group *grp, lgr;
2436 char *grpwhere = NULL, *where = NULL;
2437 sql_data_t *sd = NULL;
2438 modret_t *mr = NULL;
2439 array_header *gids = NULL, *groups = NULL;
2440 char *name = cmd->argv[0], *username = NULL;
2441 int argc, numrows = 0, res = -1;
2442 register int i = 0;
2443
2444 /* Check for NULL values */
2445 if (cmd->argv[1]) {
2446 gids = (array_header *) cmd->argv[1];
2447 }
2448
2449 if (cmd->argv[2]) {
2450 groups = (array_header *) cmd->argv[2];
2451 }
2452
2453 lpw.pw_uid = -1;
2454 lpw.pw_name = name;
2455
2456 /* Now that we have the pointers for the lists, tweak the argc field
2457 * before passing this cmd_rec on, lest we try to resolve some variable
2458 * like %r which will assume that all of the cmd_rec args are strings, as
2459 * from the client.
2460 */
2461 argc = cmd->argc;
2462 cmd->argc = 1;
2463
2464 /* Retrieve the necessary info */
2465 if (!name ||
2466 !(pw = sql_getpasswd(cmd, &lpw))) {
2467 cmd->argc = argc;
2468 return -1;
2469 }
2470
2471 /* Populate the first group ID and name */
2472 if (gids) {
2473 *((gid_t *) push_array(gids)) = pw->pw_gid;
2474 }
2475
2476 lgr.gr_gid = pw->pw_gid;
2477 lgr.gr_name = NULL;
2478
2479 if (groups &&
2480 (grp = sql_getgroup(cmd, &lgr)) != NULL) {
2481 *((char **) push_array(groups)) = pstrdup(permanent_pool, grp->gr_name);
2482 }
2483
2484 mr = sql_dispatch(sql_make_cmd(cmd->tmp_pool, 2, MOD_SQL_DEF_CONN_NAME,
2485 name), "sql_escapestring");
2486 if (check_response(mr, 0) < 0) {
2487 cmd->argc = argc;
2488 return -1;
2489 }
2490
2491 username = (char *) mr->data;
2492
2493 if (!cmap.groupcustommembers) {
2494 if (!(pr_sql_opts & SQL_OPT_USE_NORMALIZED_GROUP_SCHEMA)) {
2495
2496 /* Use a SELECT with a LIKE clause:
2497 *
2498 * SELECT groupname,gid,members FROM groups
2499 * WHERE members LIKE '%,<user>,%' OR LIKE '<user>,%' OR LIKE '%,<user>';
2500 */
2501
2502 grpwhere = pstrcat(cmd->tmp_pool,
2503 cmap.grpmembersfield, " = '", username, "' OR ",
2504 cmap.grpmembersfield, " LIKE '", username, ",%' OR ",
2505 cmap.grpmembersfield, " LIKE '%,", username, "' OR ",
2506 cmap.grpmembersfield, " LIKE '%,", username, ",%'", NULL);
2507
2508 } else {
2509 /* Use a single SELECT:
2510 *
2511 * SELECT groupname,gid,members FROM groups WHERE members = <user>';
2512 */
2513 grpwhere = pstrcat(cmd->tmp_pool,
2514 cmap.grpmembersfield, " = '", username, "'", NULL);
2515 }
2516
2517 where = sql_prepare_where(SQL_PREPARE_WHERE_FL_NO_TAGS, cmd, 2, grpwhere,
2518 sql_prepare_where(0, cmd, 1, cmap.groupwhere, NULL), NULL);
2519
2520 mr = sql_dispatch(sql_make_cmd(cmd->tmp_pool, 4, MOD_SQL_DEF_CONN_NAME,
2521 cmap.grptable, cmap.grpfields, where), "sql_select");
2522 if (check_response(mr, 0) < 0) {
2523 cmd->argc = argc;
2524 return -1;
2525 }
2526
2527 sd = (sql_data_t *) mr->data;
2528
2529 } else {
2530 array_header *ah;
2531
2532 /* The username has been escaped according to the backend database' rules
2533 * at this point.
2534 */
2535 mr = sql_lookup(sql_make_cmd(cmd->tmp_pool, 3, MOD_SQL_DEF_CONN_NAME,
2536 cmap.groupcustommembers, username));
2537 if (check_response(mr, 0) < 0) {
2538 cmd->argc = argc;
2539 return -1;
2540 }
2541
2542 ah = mr->data;
2543 sd = pcalloc(cmd->tmp_pool, sizeof(sql_data_t));
2544
2545 /* Assume the query returned N rows, 3 columns per row. */
2546 if (ah->nelts % 3 == 0) {
2547 sd->fnum = 3;
2548 sd->rnum = ah->nelts / 3;
2549
2550 if (sd->rnum > 0) {
2551 sd->data = (char **) ah->elts;
2552 }
2553
2554 } else {
2555 sql_log(DEBUG_INFO, "wrong number of columns (%d) returned by custom SQLGroupInfo members query, ignoring results", ah->nelts % 3);
2556 sd->rnum = 0;
2557 sd->data = NULL;
2558 }
2559 }
2560
2561 /* If we have no data... */
2562 if (sd->rnum == 0) {
2563 cmd->argc = argc;
2564 return -1;
2565 }
2566
2567 numrows = sd->rnum;
2568
2569 for (i = 0; i < numrows; i++) {
2570 gid_t gid;
2571 char *groupname = sd->data[(i * 3)];
2572 char *memberstr = sd->data[(i * 3) + 2], *member = NULL;
2573 array_header *members = make_array(cmd->tmp_pool, 2, sizeof(char *));
2574
2575 if (pr_str2gid(sd->data[(i * 3) +1], &gid) < 0) {
2576 gid = (gid_t) -1;
2577 }
2578
2579 *((gid_t *) push_array(gids)) = gid;
2580 *((char **) push_array(groups)) = pstrdup(permanent_pool, groupname);
2581
2582 /* For each member in the list, toss 'em into the array. no
2583 * need to copy the string -- _sql_addgroup will do it for us
2584 */
2585 for (member = strsep(&memberstr, ","); member;
2586 member = strsep(&memberstr, ",")) {
2587 if (*member == '\0') {
2588 continue;
2589 }
2590
2591 *((char **) push_array(members)) = member;
2592 }
2593
2594 /* Add this group data to the group cache. */
2595 _sql_addgroup(cmd, groupname, gid, members);
2596 }
2597
2598 if (gids &&
2599 gids->nelts > 0) {
2600 res = gids->nelts;
2601
2602 } else if (groups &&
2603 groups->nelts) {
2604 res = groups->nelts;
2605 }
2606
2607 cmd->argc = argc;
2608 return res;
2609 }
2610
2611 /* Command handlers
2612 */
2613
sql_pre_dele(cmd_rec * cmd)2614 MODRET sql_pre_dele(cmd_rec *cmd) {
2615 char *path;
2616
2617 if (cmap.engine == 0) {
2618 return PR_DECLINED(cmd);
2619 }
2620
2621 sql_dele_filesz = 0;
2622
2623 path = dir_canonical_path(cmd->tmp_pool,
2624 pr_fs_decode_path(cmd->tmp_pool, cmd->arg));
2625 if (path) {
2626 struct stat st;
2627
2628 /* Briefly cache the size of the file being deleted, so that it can be
2629 * logged properly using %b.
2630 */
2631 pr_fs_clear_cache2(path);
2632 if (pr_fsio_stat(path, &st) < 0) {
2633 sql_log(DEBUG_INFO, "%s: unable to stat '%s': %s", (char *) cmd->argv[0],
2634 path, strerror(errno));
2635
2636 } else {
2637 sql_dele_filesz = st.st_size;
2638 }
2639 }
2640
2641 return PR_DECLINED(cmd);
2642 }
2643
sql_pre_pass(cmd_rec * cmd)2644 MODRET sql_pre_pass(cmd_rec *cmd) {
2645 config_rec *c = NULL;
2646 const char *user = NULL;
2647
2648 if (cmap.engine == 0) {
2649 return PR_DECLINED(cmd);
2650 }
2651
2652 sql_log(DEBUG_FUNC, "%s", ">>> sql_pre_pass");
2653
2654 user = pr_table_get(session.notes, "mod_auth.orig-user", NULL);
2655 if (user != NULL) {
2656 config_rec *anon_config;
2657
2658 /* Use the looked-up user name to determine whether this is to be
2659 * an anonymous session.
2660 */
2661 anon_config = pr_auth_get_anon_config(cmd->pool, &user, NULL, NULL);
2662
2663 c = find_config(anon_config ? anon_config->subset : main_server->conf,
2664 CONF_PARAM, "SQLEngine", FALSE);
2665 if (c != NULL) {
2666 cmap.engine = *((int *) c->argv[0]);
2667 }
2668
2669 } else {
2670 /* Just assume the vhost config. */
2671 c = find_config(main_server->conf, CONF_PARAM, "SQLEngine", FALSE);
2672 if (c != NULL) {
2673 cmap.engine = *((int *) c->argv[0]);
2674 }
2675 }
2676
2677 sql_log(DEBUG_FUNC, "%s", "<<< sql_pre_pass");
2678 return PR_DECLINED(cmd);
2679 }
2680
sql_post_pass(cmd_rec * cmd)2681 MODRET sql_post_pass(cmd_rec *cmd) {
2682 int res;
2683
2684 if (cmap.engine == 0) {
2685 return PR_DECLINED(cmd);
2686 }
2687
2688 res = sql_getuserprimarykey(cmd, session.user);
2689 if (res < 0) {
2690 pr_trace_msg(trace_channel, 9,
2691 "error getting primary lookup key for user '%s': %s", session.user,
2692 strerror(errno));
2693 }
2694
2695 res = sql_getgroupprimarykey(cmd, session.group);
2696 if (res < 0) {
2697 pr_trace_msg(trace_channel, 9,
2698 "error getting primary lookup key for group '%s': %s", session.group,
2699 strerror(errno));
2700 }
2701
2702 return PR_DECLINED(cmd);
2703 }
2704
sql_post_stor(cmd_rec * cmd)2705 MODRET sql_post_stor(cmd_rec *cmd) {
2706 if (cmap.engine == 0) {
2707 return PR_DECLINED(cmd);
2708 }
2709
2710 sql_log(DEBUG_FUNC, "%s", ">>> sql_post_stor");
2711
2712 if (cmap.sql_fstor)
2713 _setstats(cmd, 1, 0, session.xfer.total_bytes, 0);
2714
2715 sql_log(DEBUG_FUNC, "%s", "<<< sql_post_stor");
2716 return PR_DECLINED(cmd);
2717 }
2718
sql_post_retr(cmd_rec * cmd)2719 MODRET sql_post_retr(cmd_rec *cmd) {
2720 if (cmap.engine == 0) {
2721 return PR_DECLINED(cmd);
2722 }
2723
2724 sql_log(DEBUG_FUNC, "%s", ">>> sql_post_retr");
2725
2726 if (cmap.sql_fretr)
2727 _setstats(cmd, 0, 1, 0, session.xfer.total_bytes);
2728
2729 sql_log(DEBUG_FUNC, "%s", "<<< sql_post_retr");
2730 return PR_DECLINED(cmd);
2731 }
2732
resolve_numeric_val(cmd_rec * cmd,const char * val)2733 static int resolve_numeric_val(cmd_rec *cmd, const char *val) {
2734 int idx = -1;
2735 char *ptr = NULL;
2736
2737 idx = strtol(val, &ptr, 10);
2738 if (*ptr != '\0') {
2739 return -1;
2740 }
2741
2742 if (idx < 0) {
2743 return -1;
2744 }
2745
2746 if ((cmd->argc - 3) < (unsigned int) idx) {
2747 return -1;
2748 }
2749
2750 return idx;
2751 }
2752
named_query_type(cmd_rec * cmd,char * name)2753 static char *named_query_type(cmd_rec *cmd, char *name) {
2754 config_rec *c = NULL;
2755 char *query = NULL;
2756
2757 query = pstrcat(cmd->tmp_pool, "SQLNamedQuery_", name, NULL);
2758 c = find_config(main_server->conf, CONF_PARAM, query, FALSE);
2759 if (c != NULL) {
2760 return c->argv[0];
2761 }
2762
2763 sql_log(DEBUG_FUNC, "no '%s' SQLNamedQuery found", name);
2764 errno = ENOENT;
2765 return NULL;
2766 }
2767
process_named_query(cmd_rec * cmd,char * name,int flags)2768 static modret_t *process_named_query(cmd_rec *cmd, char *name, int flags) {
2769 config_rec *c;
2770 char *conn_name, *query = NULL;
2771 char stmt[SQL_MAX_STMT_LEN+1];
2772 size_t stmt_len;
2773 modret_t *mr = NULL;
2774 int res;
2775 pool *tmp_pool;
2776 pr_jot_ctx_t *jot_ctx;
2777 struct sql_resolved *resolved;
2778
2779 sql_log(DEBUG_FUNC, ">>> process_named_query '%s'", name);
2780
2781 /* Check for a query by that name. */
2782 query = pstrcat(cmd->tmp_pool, "SQLNamedQuery_", name, NULL);
2783
2784 c = find_config(main_server->conf, CONF_PARAM, query, FALSE);
2785 if (c == NULL) {
2786 mr = PR_ERROR(cmd);
2787 sql_log(DEBUG_FUNC, "<<< process_named_query '%s'", name);
2788 return mr;
2789 }
2790
2791 conn_name = get_query_named_conn(c);
2792 set_named_conn_backend(conn_name);
2793
2794 tmp_pool = make_sub_pool(cmd->tmp_pool);
2795 jot_ctx = pcalloc(tmp_pool, sizeof(pr_jot_ctx_t));
2796 resolved = pcalloc(tmp_pool, sizeof(struct sql_resolved));
2797 resolved->bufsz = resolved->buflen = sizeof(stmt)-1;
2798 resolved->ptr = resolved->buf = stmt;
2799 resolved->conn_name = conn_name;
2800 resolved->conn_flags = flags;
2801
2802 jot_ctx->log = resolved;
2803 jot_ctx->user_data = cmd;
2804
2805 res = pr_jot_resolve_logfmt(tmp_pool, cmd, NULL, c->argv[1], jot_ctx,
2806 sql_resolve_on_meta, sql_resolve_on_default, sql_resolve_on_other);
2807 if (res < 0) {
2808 int xerrno = errno;
2809
2810 destroy_pool(tmp_pool);
2811 set_named_conn_backend(NULL);
2812
2813 if (xerrno == EIO) {
2814 return PR_ERROR_MSG(cmd, MOD_SQL_VERSION, "database error");
2815 }
2816
2817 return PR_ERROR_MSG(cmd, MOD_SQL_VERSION,
2818 "malformed reference %{?} in query");
2819 }
2820
2821 stmt_len = resolved->bufsz - resolved->buflen;
2822 stmt[stmt_len] = '\0';
2823
2824 /* Construct our return data based on the type of query */
2825 if (strcasecmp(c->argv[0], SQL_UPDATE_C) == 0) {
2826 query = pstrcat(cmd->tmp_pool, c->argv[2], " SET ", stmt, NULL);
2827 mr = sql_dispatch(sql_make_cmd(cmd->tmp_pool, 2, conn_name, query),
2828 "sql_update");
2829
2830 } else if (strcasecmp(c->argv[0], SQL_INSERT_C) == 0) {
2831 query = pstrcat(cmd->tmp_pool, "INTO ", c->argv[2], " VALUES (",
2832 stmt, ")", NULL);
2833 mr = sql_dispatch(sql_make_cmd(cmd->tmp_pool, 2, conn_name, query),
2834 "sql_insert");
2835
2836 } else if (strcasecmp(c->argv[0], SQL_FREEFORM_C) == 0) {
2837 mr = sql_dispatch(sql_make_cmd(cmd->tmp_pool, 2, conn_name, stmt),
2838 "sql_query");
2839
2840 } else if (strcasecmp(c->argv[0], SQL_SELECT_C) == 0) {
2841 mr = sql_dispatch(sql_make_cmd(cmd->tmp_pool, 2, conn_name, stmt),
2842 "sql_select");
2843
2844 if (MODRET_ISHANDLED(mr) &&
2845 MODRET_HASDATA(mr) &&
2846 pr_trace_get_level(trace_channel) >= 9) {
2847 register unsigned long i, idx;
2848 sql_data_t *sd;
2849
2850 sd = mr->data;
2851
2852 pr_trace_msg(trace_channel, 9, "SQLNamedQuery %s results:", name);
2853 pr_trace_msg(trace_channel, 9, " row count: %lu", sd->rnum);
2854 pr_trace_msg(trace_channel, 9, " col count: %lu", sd->fnum);
2855
2856 for (i = 0, idx = 0; i < sd->rnum; i++) {
2857 register unsigned long j;
2858
2859 pr_trace_msg(trace_channel, 9, " row #%lu:", i+1);
2860 for (j = 0; j < sd->fnum; j++) {
2861 pr_trace_msg(trace_channel, 9, " col #%lu: '%s'", j+1,
2862 sd->data[idx++]);
2863 }
2864 }
2865 }
2866
2867 } else {
2868 mr = PR_ERROR_MSG(cmd, MOD_SQL_VERSION, "unknown NamedQuery type");
2869 }
2870
2871 set_named_conn_backend(NULL);
2872 destroy_pool(tmp_pool);
2873
2874 sql_log(DEBUG_FUNC, "<<< process_named_query '%s'", name);
2875 return mr;
2876 }
2877
process_sqllog(cmd_rec * cmd,config_rec * c,const char * label,int flags)2878 MODRET process_sqllog(cmd_rec *cmd, config_rec *c, const char *label,
2879 int flags) {
2880 char *query_name = NULL, *query_type = NULL;
2881 modret_t *mr = NULL;
2882
2883 query_name = c->argv[0];
2884
2885 sql_log(DEBUG_FUNC, ">>> %s (%s)", label, c->name);
2886
2887 query_type = named_query_type(cmd, query_name);
2888 if (query_type != NULL) {
2889 if (strcasecmp(query_type, SQL_UPDATE_C) == 0 ||
2890 strcasecmp(query_type, SQL_FREEFORM_C) == 0 ||
2891 strcasecmp(query_type, SQL_INSERT_C) == 0) {
2892 mr = process_named_query(cmd, query_name, flags);
2893 if (check_response(mr, flags) < 0) {
2894 return mr;
2895 }
2896
2897 } else {
2898 sql_log(DEBUG_WARN, "named query '%s' is not an INSERT, UPDATE, or "
2899 "FREEFORM query", query_name);
2900 }
2901
2902 } else {
2903 sql_log(DEBUG_WARN, "named query '%s' cannot be found", query_name);
2904 }
2905
2906 sql_log(DEBUG_FUNC, "<<< %s (%s)", label, c->name);
2907 return mr;
2908 }
2909
eventlog_master(const char * event_name)2910 static int eventlog_master(const char *event_name) {
2911 char *name = NULL;
2912 cmd_rec *cmd = NULL;
2913 config_rec *c = NULL;
2914 modret_t *mr = NULL;
2915
2916 if (!(cmap.engine & SQL_ENGINE_FL_LOG)) {
2917 return 0;
2918 }
2919
2920 /* Need to create fake cmd_rec for dispatching, since we need
2921 * cmd->pool, cmd->tmp_pool. The cmd_rec MUST have
2922 * fake/unknown name (i.e. cmd->argv[0], cmd->cmd_id), so that it does
2923 * not run afoul of other logging variables.
2924 */
2925 cmd = sql_make_cmd(session.pool, 1, "EVENT");
2926 name = pstrcat(cmd->tmp_pool, "SQLLog_Event_", event_name, NULL);
2927
2928 c = find_config(main_server->conf, CONF_PARAM, name, FALSE);
2929 while (c != NULL) {
2930 int flags = 0;
2931
2932 pr_signals_handle();
2933
2934 if (c->argc == 3 &&
2935 strncmp(c->argv[2], "ignore", 7) == 0) {
2936 flags |= SQL_LOG_FL_IGNORE_ERRORS;
2937 }
2938
2939 pr_trace_msg(trace_channel, 12,
2940 "executing SQLNamedQuery '%s' for event '%s'", (char *) c->argv[0],
2941 event_name);
2942 mr = process_sqllog(cmd, c, "eventlog_master", flags);
2943 if (mr != NULL &&
2944 MODRET_ISERROR(mr)) {
2945 SQL_FREE_CMD(cmd);
2946 return -1;
2947 }
2948
2949 c = find_config_next(c, c->next, CONF_PARAM, name, FALSE);
2950 }
2951
2952 SQL_FREE_CMD(cmd);
2953 return 0;
2954 }
2955
log_master(cmd_rec * cmd)2956 MODRET log_master(cmd_rec *cmd) {
2957 char *name = NULL;
2958 config_rec *c = NULL;
2959 modret_t *mr = NULL;
2960
2961 if (!(cmap.engine & SQL_ENGINE_FL_LOG)) {
2962 return PR_DECLINED(cmd);
2963 }
2964
2965 /* Ignore EXIT commands (as from mod_log) here; we handle them differently
2966 * in the 'core.exit' event lister.
2967 */
2968 if (pr_cmd_strcmp(cmd, "EXIT") == 0) {
2969 return PR_DECLINED(cmd);
2970 }
2971
2972 /* handle explicit queries */
2973 name = pstrcat(cmd->tmp_pool, "SQLLog_", cmd->argv[0], NULL);
2974
2975 c = find_config(main_server->conf, CONF_PARAM, name, FALSE);
2976 while (c != NULL) {
2977 int flags = 0;
2978
2979 pr_signals_handle();
2980
2981 if (c->argc == 2 &&
2982 strncmp(c->argv[1], "ignore", 7) == 0) {
2983 flags |= SQL_LOG_FL_IGNORE_ERRORS;
2984 }
2985
2986 mr = process_sqllog(cmd, c, "log_master", flags);
2987 if (mr != NULL &&
2988 MODRET_ISERROR(mr)) {
2989
2990 /* We always return DECLINED if we're in the LOG_CMD/LOG_CMD_ERR phase,
2991 * regardless of the SQLLog processing outcome (Bug#3633).
2992 */
2993 if (session.curr_phase == LOG_CMD ||
2994 session.curr_phase == LOG_CMD_ERR) {
2995 return PR_DECLINED(cmd);
2996 }
2997
2998 return mr;
2999 }
3000
3001 c = find_config_next(c, c->next, CONF_PARAM, name, FALSE);
3002 }
3003
3004 /* handle implicit queries */
3005 name = pstrcat(cmd->tmp_pool, "SQLLog_*", NULL);
3006
3007 c = find_config(main_server->conf, CONF_PARAM, name, FALSE);
3008 while (c != NULL) {
3009 int flags = 0;
3010
3011 pr_signals_handle();
3012
3013 if (c->argc == 2 &&
3014 strncmp(c->argv[1], "ignore", 7) == 0) {
3015 flags |= SQL_LOG_FL_IGNORE_ERRORS;
3016 }
3017
3018 mr = process_sqllog(cmd, c, "log_master", flags);
3019 if (mr != NULL &&
3020 MODRET_ISERROR(mr)) {
3021
3022 /* We always return DECLINED if we're in the LOG_CMD/LOG_CMD_ERR phase,
3023 * regardless of the SQLLog processing outcome (Bug#3633).
3024 */
3025 if (session.curr_phase == LOG_CMD ||
3026 session.curr_phase == LOG_CMD_ERR) {
3027 return PR_DECLINED(cmd);
3028 }
3029
3030 return mr;
3031 }
3032
3033 c = find_config_next(c, c->next, CONF_PARAM, name, FALSE);
3034 }
3035
3036 return PR_DECLINED(cmd);
3037 }
3038
err_master(cmd_rec * cmd)3039 MODRET err_master(cmd_rec *cmd) {
3040 char *name = NULL;
3041 config_rec *c = NULL;
3042 modret_t *mr = NULL;
3043
3044 if (!(cmap.engine & SQL_ENGINE_FL_LOG)) {
3045 return PR_DECLINED(cmd);
3046 }
3047
3048 /* handle explicit errors */
3049 name = pstrcat(cmd->tmp_pool, "SQLLog_ERR_", cmd->argv[0], NULL);
3050
3051 c = find_config(main_server->conf, CONF_PARAM, name, FALSE);
3052 while (c != NULL) {
3053 int flags = 0;
3054
3055 pr_signals_handle();
3056
3057 if (c->argc == 2 &&
3058 strncmp(c->argv[1], "ignore", 7) == 0) {
3059 flags |= SQL_LOG_FL_IGNORE_ERRORS;
3060 }
3061
3062 mr = process_sqllog(cmd, c, "err_master", flags);
3063 if (mr != NULL &&
3064 MODRET_ISERROR(mr)) {
3065
3066 /* We always return DECLINED if we're in the LOG_CMD/LOG_CMD_ERR phase,
3067 * regardless of the SQLLog processing outcome (Bug#3633).
3068 */
3069 if (session.curr_phase == LOG_CMD ||
3070 session.curr_phase == LOG_CMD_ERR) {
3071 return PR_DECLINED(cmd);
3072 }
3073
3074 return mr;
3075 }
3076
3077 c = find_config_next(c, c->next, CONF_PARAM, name, FALSE);
3078 }
3079
3080 /* handle implicit errors */
3081 name = pstrcat(cmd->tmp_pool, "SQLLog_ERR_*", NULL);
3082
3083 c = find_config(main_server->conf, CONF_PARAM, name, FALSE);
3084 while (c != NULL) {
3085 int flags = 0;
3086
3087 pr_signals_handle();
3088
3089 if (c->argc == 2 &&
3090 strncmp(c->argv[1], "ignore", 7) == 0) {
3091 flags |= SQL_LOG_FL_IGNORE_ERRORS;
3092 }
3093
3094 mr = process_sqllog(cmd, c, "err_master", flags);
3095 if (mr != NULL &&
3096 MODRET_ISERROR(mr)) {
3097
3098 /* We always return DECLINED if we're in the LOG_CMD/LOG_CMD_ERR phase,
3099 * regardless of the SQLLog processing outcome (Bug#3633).
3100 */
3101 if (session.curr_phase == LOG_CMD ||
3102 session.curr_phase == LOG_CMD_ERR) {
3103 return PR_DECLINED(cmd);
3104 }
3105
3106 return mr;
3107 }
3108
3109 c = find_config_next(c, c->next, CONF_PARAM, name, FALSE);
3110 }
3111
3112 return PR_DECLINED(cmd);
3113 }
3114
showinfo_on_meta(pool * p,pr_jot_ctx_t * jot_ctx,unsigned char logfmt_id,const char * jot_hint,const void * val)3115 static int showinfo_on_meta(pool *p, pr_jot_ctx_t *jot_ctx,
3116 unsigned char logfmt_id, const char *jot_hint, const void *val) {
3117 int res = 0;
3118 struct sql_resolved *resolved;
3119 cmd_rec *cmd;
3120
3121 resolved = jot_ctx->log;
3122 cmd = (cmd_rec *) jot_ctx->user_data;
3123
3124 if (resolved->buflen > 0) {
3125 /* Note: we can cheat, and reuse the sql_resolve_on_meta() function for
3126 * almost all of this.
3127 */
3128 if (logfmt_id == LOGFMT_META_CUSTOM) {
3129 const char *text;
3130 size_t text_len = 0;
3131 char *query_name, *query_type;
3132 modret_t *mr = NULL;
3133 sql_data_t *sd = NULL;
3134
3135 /* If this is not a SELECT query, skip it. */
3136 query_name = (char *) val;
3137 query_type = named_query_type(cmd, query_name);
3138 if (query_type == NULL ||
3139 (strcasecmp(query_type, SQL_SELECT_C) != 0 &&
3140 strcasecmp(query_type, SQL_FREEFORM_C) != 0)) {
3141 return 0;
3142 }
3143
3144 mr = process_named_query(cmd, query_name, 0);
3145 if (check_response(mr, 0) < 0) {
3146 errno = EPERM;
3147 return -1;
3148 }
3149
3150 sd = mr->data;
3151 if (sd->rnum == 0 ||
3152 sd->data[0] == NULL) {
3153 /* No data returned. */
3154 errno = ENOENT;
3155 return -1;
3156 }
3157
3158 text = sd->data[0];
3159
3160 /* Treat the text "null" the same as a real null, and ignore it. */
3161 if (strcasecmp(text, "null") == 0) {
3162 errno = ENOENT;
3163 return -1;
3164 }
3165
3166 text_len = strlen(text);
3167 res = sql_resolved_append_text(p, resolved, text, text_len);
3168
3169 } else {
3170 res = sql_resolve_on_meta(p, jot_ctx, logfmt_id, jot_hint, val);
3171 }
3172 }
3173
3174 return res;
3175 }
3176
get_showinfo_query_text(cmd_rec * cmd,unsigned char * logfmt,const char * conn_name,size_t * text_len)3177 static char *get_showinfo_query_text(cmd_rec *cmd, unsigned char *logfmt,
3178 const char *conn_name, size_t *text_len) {
3179 char results[SQL_MAX_STMT_LEN+1], *text = NULL;
3180 size_t results_len = 0;
3181 int res;
3182 pool *tmp_pool;
3183 pr_jot_ctx_t *jot_ctx;
3184 struct sql_resolved *resolved;
3185
3186 tmp_pool = make_sub_pool(cmd->tmp_pool);
3187 jot_ctx = pcalloc(tmp_pool, sizeof(pr_jot_ctx_t));
3188 resolved = pcalloc(tmp_pool, sizeof(struct sql_resolved));
3189 resolved->bufsz = resolved->buflen = sizeof(results)-1;
3190 resolved->ptr = resolved->buf = results;
3191 resolved->conn_name = conn_name;
3192
3193 jot_ctx->log = resolved;
3194 jot_ctx->user_data = cmd;
3195
3196 res = pr_jot_resolve_logfmt(tmp_pool, cmd, NULL, logfmt, jot_ctx,
3197 showinfo_on_meta, sql_resolve_on_default, sql_resolve_on_other);
3198 if (res < 0) {
3199 if (errno == EIO) {
3200 return NULL;
3201 }
3202
3203 /* For any other reason, the resolver terminated early; we do not
3204 * want to use anything that may be in the buffer.
3205 */
3206 results_len = 0;
3207
3208 } else {
3209 results_len = resolved->bufsz - resolved->buflen;
3210 }
3211
3212 results[results_len] = '\0';
3213 text = pstrndup(cmd->tmp_pool, results, results_len);
3214 *text_len = results_len;
3215
3216 destroy_pool(tmp_pool);
3217 return text;
3218 }
3219
info_master(cmd_rec * cmd)3220 MODRET info_master(cmd_rec *cmd) {
3221 char *name = NULL;
3222 config_rec *c = NULL;
3223 char *resp_code = NULL;
3224
3225 if (!(cmap.engine & SQL_ENGINE_FL_LOG)) {
3226 return PR_DECLINED(cmd);
3227 }
3228
3229 /* process explicit handlers */
3230 name = pstrcat(cmd->tmp_pool, "SQLShowInfo_", cmd->argv[0], NULL);
3231
3232 c = find_config(main_server->conf, CONF_PARAM, name, FALSE);
3233 while (c != NULL) {
3234 char *conn_name, *text = NULL;
3235 size_t text_len = 0;
3236
3237 pr_signals_handle();
3238
3239 sql_log(DEBUG_FUNC, ">>> info_master (%s)", name);
3240
3241 /* We now have at least one config_rec. Take the output string from
3242 * each, and process it: resolve tags, and when we find a named query,
3243 * run it and get info from it.
3244 */
3245
3246 conn_name = get_query_named_conn(c);
3247 set_named_conn_backend(conn_name);
3248
3249 text = get_showinfo_query_text(cmd, c->argv[1], conn_name, &text_len);
3250 set_named_conn_backend(NULL);
3251
3252 /* Add the response, if we have one. */
3253 if (text != NULL &&
3254 text_len > 0) {
3255 /* We keep track of the response code used, as we will need it when
3256 * flushing the added lines out to the client.
3257 */
3258 resp_code = c->argv[0];
3259 pr_response_add(resp_code, "%s", text);
3260 }
3261
3262 sql_log(DEBUG_FUNC, "<<< info_master (%s)", name);
3263
3264 c = find_config_next(c, c->next, CONF_PARAM, name, FALSE);
3265 }
3266
3267 /* process implicit handlers */
3268 name = pstrdup(cmd->tmp_pool, "SQLShowInfo_*");
3269
3270 c = find_config(main_server->conf, CONF_PARAM, name, FALSE);
3271 while (c != NULL) {
3272 char *conn_name, *text = NULL;
3273 size_t text_len = 0;
3274
3275 pr_signals_handle();
3276
3277 sql_log(DEBUG_FUNC, ">>> info_master (%s)", name);
3278
3279 conn_name = get_query_named_conn(c);
3280 set_named_conn_backend(conn_name);
3281
3282 text = get_showinfo_query_text(cmd, c->argv[1], conn_name, &text_len);
3283 set_named_conn_backend(NULL);
3284
3285 /* Add the response, if we have one. */
3286 if (text != NULL &&
3287 text_len > 0) {
3288 /* We keep track of the response code used, as we will need it when
3289 * flushing the added lines out to the client.
3290 */
3291 resp_code = c->argv[0];
3292 pr_response_add(resp_code, "%s", text);
3293 }
3294
3295 sql_log(DEBUG_FUNC, "<<< info_master (%s)", name);
3296
3297 c = find_config_next(c, c->next, CONF_PARAM, name, FALSE);
3298 }
3299
3300 return PR_DECLINED(cmd);
3301 }
3302
errinfo_master(cmd_rec * cmd)3303 MODRET errinfo_master(cmd_rec *cmd) {
3304 char *name = NULL;
3305 config_rec *c = NULL;
3306 char *resp_code = NULL;
3307
3308 if (!(cmap.engine & SQL_ENGINE_FL_LOG)) {
3309 return PR_DECLINED(cmd);
3310 }
3311
3312 /* process explicit handlers */
3313 name = pstrcat(cmd->tmp_pool, "SQLShowInfo_ERR_", cmd->argv[0], NULL);
3314
3315 c = find_config(main_server->conf, CONF_PARAM, name, FALSE);
3316 while (c != NULL) {
3317 char *conn_name, *text = NULL;
3318 size_t text_len = 0;
3319
3320 pr_signals_handle();
3321
3322 sql_log(DEBUG_FUNC, ">>> errinfo_master (%s)", name);
3323
3324 /* We now have at least one config_rec. Take the output string from
3325 * each, and process it: resolve tags, and when we find a named query,
3326 * run it and get info from it.
3327 */
3328
3329 conn_name = get_query_named_conn(c);
3330 set_named_conn_backend(conn_name);
3331
3332 pr_trace_msg(trace_channel, 15, "processing SQLShowInfo ERR_%s",
3333 (char *) cmd->argv[0]);
3334 text = get_showinfo_query_text(cmd, c->argv[1], conn_name, &text_len);
3335 set_named_conn_backend(NULL);
3336
3337 /* Add the response, if we have one. */
3338 if (text != NULL &&
3339 text_len > 0) {
3340 /* We keep track of the response code used, as we will need it when
3341 * flushing the added lines out to the client.
3342 */
3343 resp_code = c->argv[0];
3344
3345 if (*resp_code == '4' ||
3346 *resp_code == '5') {
3347 pr_trace_msg(trace_channel, 15,
3348 "adding error response code %s, msg '%.*s' for SQLShowInfo ERR_%s",
3349 resp_code, (int) text_len, text, (char *) cmd->argv[0]);
3350
3351 pr_response_add_err(resp_code, "%.*s", (int) text_len, text);
3352
3353 } else {
3354 pr_trace_msg(trace_channel, 15,
3355 "adding response code %s, msg '%.*s' for SQLShowInfo ERR_%s",
3356 resp_code, (int) text_len, text, (char *) cmd->argv[0]);
3357
3358 pr_response_add(resp_code, "%.*s", (int) text_len, text);
3359 }
3360 }
3361
3362 sql_log(DEBUG_FUNC, "<<< errinfo_master (%s)", name);
3363
3364 c = find_config_next(c, c->next, CONF_PARAM, name, FALSE);
3365 }
3366
3367 /* process implicit handlers */
3368 name = pstrcat(cmd->tmp_pool, "SQLShowInfo_ERR_*", NULL);
3369
3370 c = find_config(main_server->conf, CONF_PARAM, name, FALSE);
3371 while (c != NULL) {
3372 char *conn_name, *text = NULL;
3373 size_t text_len = 0;
3374
3375 pr_signals_handle();
3376
3377 sql_log(DEBUG_FUNC, ">>> errinfo_master (%s)", name);
3378
3379 conn_name = get_query_named_conn(c);
3380 set_named_conn_backend(conn_name);
3381
3382 text = get_showinfo_query_text(cmd, c->argv[1], conn_name, &text_len);
3383 set_named_conn_backend(NULL);
3384
3385 /* Add the response, if we have one. */
3386 if (text != NULL &&
3387 text_len > 0) {
3388 /* We keep track of the response code used, as we will need it when
3389 * flushing the added lines out to the client.
3390 */
3391 resp_code = c->argv[0];
3392
3393 if (*resp_code == '4' ||
3394 *resp_code == '5') {
3395 pr_trace_msg(trace_channel, 15,
3396 "adding error response code %s, msg '%.*s' for SQLShowInfo ERR_*",
3397 resp_code, (int) text_len, text);
3398
3399 pr_response_add_err(resp_code, "%.*s", (int) text_len, text);
3400
3401 } else {
3402 pr_trace_msg(trace_channel, 15,
3403 "adding response code %s, msg '%.*s' for SQLShowInfo ERR_*",
3404 resp_code, (int) text_len, text);
3405
3406 pr_response_add(resp_code, "%.*s", (int) text_len, text);
3407 }
3408 }
3409
3410 sql_log(DEBUG_FUNC, "<<< errinfo_master (%s)", name);
3411
3412 c = find_config_next(c, c->next, CONF_PARAM, name, FALSE);
3413 }
3414
3415 return PR_DECLINED(cmd);
3416 }
3417
sql_cleanup(cmd_rec * cmd)3418 MODRET sql_cleanup(cmd_rec *cmd) {
3419 modret_t *res;
3420
3421 sql_log(DEBUG_FUNC, "%s", ">>> sql_cleanup");
3422
3423 res = sql_dispatch(cmd, "sql_cleanup");
3424 if (check_response(res, 0) < 0) {
3425 sql_log(DEBUG_FUNC, "%s", "<<< sql_cleanup");
3426 return res;
3427 }
3428
3429 sql_log(DEBUG_FUNC, "%s", "<<< sql_cleanup");
3430 return res;
3431 }
3432
sql_closeconn(cmd_rec * cmd)3433 MODRET sql_closeconn(cmd_rec *cmd) {
3434 modret_t *res;
3435
3436 sql_log(DEBUG_FUNC, "%s", ">>> sql_closeconn");
3437 res = sql_dispatch(cmd, "sql_close");
3438 sql_log(DEBUG_FUNC, "%s", "<<< sql_closeconn");
3439
3440 return res;
3441 }
3442
sql_defineconn(cmd_rec * cmd)3443 MODRET sql_defineconn(cmd_rec *cmd) {
3444 modret_t *res;
3445
3446 sql_log(DEBUG_FUNC, "%s", ">>> sql_defineconn");
3447 res = sql_dispatch(cmd, "sql_defineconnection");
3448 sql_log(DEBUG_FUNC, "%s", "<<< sql_defineconn");
3449
3450 return res;
3451 }
3452
sql_load_backend(cmd_rec * cmd)3453 MODRET sql_load_backend(cmd_rec *cmd) {
3454 modret_t *res;
3455
3456 sql_log(DEBUG_FUNC, "%s", ">>> sql_load_backend");
3457
3458 if (cmd->argc == 1) {
3459 sql_set_backend(cmd->argv[0]);
3460
3461 } else {
3462 sql_set_backend(NULL);
3463 }
3464
3465 res = mod_create_data(cmd, NULL);
3466
3467 sql_log(DEBUG_FUNC, "%s", "<<< sql_load_backend");
3468 return res;
3469 }
3470
sql_openconn(cmd_rec * cmd)3471 MODRET sql_openconn(cmd_rec *cmd) {
3472 modret_t *res;
3473
3474 sql_log(DEBUG_FUNC, "%s", ">>> sql_openconn");
3475 res = sql_dispatch(cmd, "sql_open");
3476 sql_log(DEBUG_FUNC, "%s", "<<< sql_openconn");
3477
3478 return res;
3479 }
3480
sql_prepare(cmd_rec * cmd)3481 MODRET sql_prepare(cmd_rec *cmd) {
3482 modret_t *res;
3483
3484 sql_log(DEBUG_FUNC, "%s", ">>> sql_prepare");
3485 res = sql_dispatch(cmd, "sql_prepare");
3486 sql_log(DEBUG_FUNC, "%s", "<<< sql_prepare");
3487
3488 return res;
3489 }
3490
sql_select(cmd_rec * cmd)3491 MODRET sql_select(cmd_rec *cmd) {
3492 modret_t *res;
3493
3494 sql_log(DEBUG_FUNC, "%s", ">>> sql_select");
3495 res = sql_dispatch(cmd, "sql_select");
3496 sql_log(DEBUG_FUNC, "%s", "<<< sql_select");
3497
3498 return res;
3499 }
3500
3501 /* sql_lookup: used by third-party modules to get data via a SQL query.
3502 * Third party module must pass a legitimate cmd_rec (including tmp_pool),
3503 * and the cmd_rec must have only one argument: the name of a SQLNamedQuery.
3504 *
3505 * Returns:
3506 *
3507 * DECLINED if mod_sql isn't on
3508 * ERROR if named query doesn't exist
3509 *
3510 * SHUTS DOWN if query caused an error
3511 *
3512 * otherwise:
3513 *
3514 * array_header * in the data slot with the returned data. It is up to the
3515 * calling function to know how many pieces of data to expect, and how to
3516 * parse them.
3517 */
sql_lookup(cmd_rec * cmd)3518 MODRET sql_lookup(cmd_rec *cmd) {
3519 char *type = NULL;
3520 modret_t *mr = NULL;
3521 sql_data_t *sd = NULL;
3522 array_header *ah = NULL;
3523
3524 if (cmap.engine == 0) {
3525 return PR_DECLINED(cmd);
3526 }
3527
3528 if (cmd->argc < 1) {
3529 return PR_ERROR(cmd);
3530 }
3531
3532 sql_log(DEBUG_FUNC, "%s", ">>> sql_lookup");
3533
3534 type = named_query_type(cmd, cmd->argv[1]);
3535 if (type && (strcasecmp(type, SQL_SELECT_C) == 0 ||
3536 strcasecmp(type, SQL_FREEFORM_C) == 0)) {
3537 mr = process_named_query(cmd, cmd->argv[1], 0);
3538
3539 if (mr != NULL &&
3540 !MODRET_ISERROR(mr)) {
3541 register unsigned int i;
3542
3543 sd = (sql_data_t *) mr->data;
3544
3545 ah = make_array(session.pool, (sd->rnum * sd->fnum) , sizeof(char *));
3546
3547 /* The right way to do this is to preserve the abstraction of the array
3548 * header so things don't blow up when it gets freed.
3549 */
3550 for (i = 0; i < (sd->rnum * sd->fnum); i++) {
3551 *((char **) push_array(ah)) = sd->data[i];
3552 }
3553
3554 mr = mod_create_data(cmd, (void *) ah);
3555
3556 } else {
3557 /* We have an error. Log it and die. */
3558 if (check_response(mr, 0) < 0) {
3559 sql_log(DEBUG_FUNC, "%s", "<<< sql_lookup");
3560 return mr;
3561 }
3562 }
3563
3564 } else {
3565 mr = PR_ERROR(cmd);
3566 }
3567
3568 sql_log(DEBUG_FUNC, "%s", "<<< sql_lookup");
3569 return mr;
3570 }
3571
sql_change(cmd_rec * cmd)3572 MODRET sql_change(cmd_rec *cmd) {
3573 char *type = NULL;
3574 modret_t *mr = NULL;
3575
3576 if (cmap.engine == 0) {
3577 return PR_DECLINED(cmd);
3578 }
3579
3580 if (cmd->argc < 1) {
3581 return PR_ERROR(cmd);
3582 }
3583
3584 sql_log(DEBUG_FUNC, "%s", ">>> sql_change");
3585
3586 type = named_query_type(cmd, cmd->argv[1]);
3587 if (type && ((!strcasecmp(type, SQL_INSERT_C)) ||
3588 (!strcasecmp(type, SQL_UPDATE_C)) ||
3589 (!strcasecmp(type, SQL_FREEFORM_C)))) {
3590 /* fixup the cmd_rec */
3591
3592 mr = process_named_query(cmd, cmd->argv[1], 0);
3593 if (check_response(mr, 0) < 0) {
3594 sql_log(DEBUG_FUNC, "%s", "<<< sql_change");
3595 return mr;
3596 }
3597
3598 } else {
3599 mr = PR_ERROR(cmd);
3600 }
3601
3602 sql_log(DEBUG_FUNC, "%s", "<<< sql_change");
3603 return mr;
3604 }
3605
sql_escapestr(cmd_rec * cmd)3606 MODRET sql_escapestr(cmd_rec *cmd) {
3607 modret_t *mr;
3608
3609 sql_log(DEBUG_FUNC, "%s", ">>> sql_escapestr");
3610
3611 mr = sql_dispatch(sql_make_cmd(cmd->tmp_pool, 2, MOD_SQL_DEF_CONN_NAME,
3612 cmd->argv[0]), "sql_escapestring");
3613 if (check_response(mr, 0) < 0) {
3614 sql_log(DEBUG_FUNC, "%s", "<<< sql_escapestr");
3615 return mr;
3616 }
3617
3618 sql_log(DEBUG_FUNC, "%s", "<<< sql_escapestr");
3619 return mr;
3620 }
3621
3622 /* SQLKeepAlive timer callback. */
sql_keepalive_cb(CALLBACK_FRAME)3623 static int sql_keepalive_cb(CALLBACK_FRAME) {
3624 pool *tmp_pool;
3625 cmd_rec *cmd;
3626
3627 tmp_pool = make_sub_pool(session.pool);
3628 pr_pool_tag(tmp_pool, "SQL keepalive pool");
3629 cmd = sql_make_cmd(tmp_pool, 2, MOD_SQL_DEF_CONN_NAME, sql_keepalive_stmt);
3630 (void) sql_dispatch(cmd, "sql_query");
3631
3632 return 1;
3633 }
3634
3635 /* Auth Handlers.
3636 */
3637
sql_auth_setpwent(cmd_rec * cmd)3638 MODRET sql_auth_setpwent(cmd_rec *cmd) {
3639 sql_data_t *sd = NULL;
3640 modret_t *mr = NULL;
3641 char *where = NULL;
3642 int i = 0;
3643 unsigned long cnt = 0;
3644
3645 char *username = NULL;
3646 char *password = NULL;
3647 char *shell = NULL;
3648 char *dir = NULL;
3649 uid_t uid = 0;
3650 gid_t gid = 0;
3651
3652 struct passwd lpw;
3653
3654 if (!SQL_USERSET ||
3655 !(cmap.engine & SQL_ENGINE_FL_AUTH)) {
3656 return PR_DECLINED(cmd);
3657 }
3658
3659 sql_log(DEBUG_FUNC, "%s", ">>> cmd_setpwent");
3660
3661 /* if we've already filled the passwd cache, just reset the curr_passwd */
3662 if (cmap.passwd_cache_filled) {
3663 cmap.curr_passwd = passwd_name_cache->head;
3664 sql_log(DEBUG_FUNC, "%s", "<<< cmd_setpwent");
3665 return PR_DECLINED(cmd);
3666 }
3667
3668 /* single select or not? */
3669 if (SQL_FASTUSERS) {
3670 /* retrieve our list of users */
3671
3672 if (!cmap.usercustomusersetfast) {
3673 where = sql_prepare_where(0, cmd, 1, cmap.userwhere, NULL);
3674
3675 mr = sql_dispatch(sql_make_cmd(cmd->tmp_pool, 4, MOD_SQL_DEF_CONN_NAME,
3676 cmap.usrtable, cmap.usrfields, where), "sql_select");
3677 if (check_response(mr, 0) < 0) {
3678 return mr;
3679 }
3680
3681 sd = (sql_data_t *) mr->data;
3682
3683 } else {
3684 mr = sql_lookup(sql_make_cmd(cmd->tmp_pool, 2, MOD_SQL_DEF_CONN_NAME,
3685 cmap.usercustomusersetfast));
3686 if (check_response(mr, 0) < 0) {
3687 return mr;
3688 }
3689
3690 if (MODRET_HASDATA(mr)) {
3691 array_header *ah = (array_header *) mr->data;
3692 sd = pcalloc(cmd->tmp_pool, sizeof(sql_data_t));
3693
3694 /* Assume the query returned 6 columns per row. */
3695 sd->fnum = 6;
3696 sd->rnum = ah->nelts / 6;
3697 sd->data = (char **) ah->elts;
3698
3699 } else {
3700 sd = pcalloc(cmd->tmp_pool, sizeof(sql_data_t));
3701 sd->rnum = 0;
3702 }
3703 }
3704
3705 /* Walk through the array, adding users to the cache */
3706 if (sd != NULL) {
3707 for (i = 0, cnt = 0; cnt < sd->rnum; cnt++) {
3708 username = sd->data[i++];
3709
3710 /* if the username is NULL, skip it */
3711 if (username == NULL)
3712 continue;
3713
3714 password = sd->data[i++];
3715
3716 uid = cmap.defaultuid;
3717 if (cmap.uidfield) {
3718 if (sd->data[i]) {
3719 if (pr_str2uid(sd->data[i++], &uid) < 0) {
3720 uid = cmap.defaultuid;
3721 }
3722
3723 } else {
3724 i++;
3725 }
3726 }
3727
3728 gid = cmap.defaultgid;
3729 if (cmap.gidfield) {
3730 if (sd->data[i]) {
3731 if (pr_str2gid(sd->data[i++], &gid) < 0) {
3732 gid = cmap.defaultgid;
3733 }
3734
3735 } else {
3736 i++;
3737 }
3738 }
3739
3740 dir = cmap.defaulthomedir;
3741 if (sd->data[i]) {
3742 if (strncmp(sd->data[i], "", 2) == 0 ||
3743 strncmp(sd->data[i], "NULL", 5) == 0) {
3744 /* Leave dir pointing to the SQLDefaultHomedir, if any. */
3745 i++;
3746
3747 } else {
3748 dir = sd->data[i++];
3749 }
3750 }
3751
3752 if (cmap.shellfield) {
3753 shell = sd->data[i++];
3754
3755 } else {
3756 shell = "";
3757 }
3758
3759 if (uid < cmap.minuseruid) {
3760 sql_log(DEBUG_INFO, "user UID %s below SQLMinUserUID %s, using "
3761 "SQLDefaultUID %s", pr_uid2str(cmd->tmp_pool, uid),
3762 pr_uid2str(cmd->tmp_pool, cmap.minuseruid),
3763 pr_uid2str(cmd->tmp_pool, cmap.defaultuid));
3764 uid = cmap.defaultuid;
3765 }
3766
3767 if (gid < cmap.minusergid) {
3768 sql_log(DEBUG_INFO, "user GID %s below SQLMinUserGID %s, using "
3769 "SQLDefaultGID %s", pr_gid2str(cmd->tmp_pool, gid),
3770 pr_gid2str(cmd->tmp_pool, cmap.minusergid),
3771 pr_gid2str(cmd->tmp_pool, cmap.defaultgid));
3772 gid = cmap.defaultgid;
3773 }
3774
3775 _sql_addpasswd(cmd, username, password, uid, gid, shell, dir);
3776 }
3777 }
3778
3779 } else {
3780 /* Retrieve our list of users */
3781
3782 if (!cmap.usercustomuserset) {
3783 where = sql_prepare_where(0, cmd, 1, cmap.userwhere, NULL);
3784
3785 mr = sql_dispatch(sql_make_cmd(cmd->tmp_pool, 4, MOD_SQL_DEF_CONN_NAME,
3786 cmap.usrtable, cmap.usrfield, where), "sql_select");
3787 if (check_response(mr, 0) < 0) {
3788 return mr;
3789 }
3790
3791 sd = (sql_data_t *) mr->data;
3792
3793 } else {
3794 mr = sql_lookup(sql_make_cmd(cmd->tmp_pool, 2, MOD_SQL_DEF_CONN_NAME,
3795 cmap.usercustomuserset));
3796 if (check_response(mr, 0) < 0) {
3797 return mr;
3798 }
3799
3800 if (MODRET_HASDATA(mr)) {
3801 array_header *ah = (array_header *) mr->data;
3802 sd = pcalloc(cmd->tmp_pool, sizeof(sql_data_t));
3803
3804 /* Assume the query only returned 1 column per row. */
3805 sd->fnum = 1;
3806 sd->rnum = ah->nelts;
3807 sd->data = (char **) ah->elts;
3808 }
3809 }
3810
3811 if (sd != NULL) {
3812 for (cnt = 0; cnt < sd->rnum; cnt++) {
3813 username = sd->data[cnt];
3814
3815 /* if the username is NULL for whatever reason, skip it */
3816 if (username == NULL)
3817 continue;
3818
3819 /* otherwise, add it to the cache */
3820 lpw.pw_uid = -1;
3821 lpw.pw_name = username;
3822 sql_getpasswd(cmd, &lpw);
3823 }
3824 }
3825 }
3826
3827 cmap.passwd_cache_filled = 1;
3828 cmap.curr_passwd = passwd_name_cache->head;
3829
3830 sql_log(DEBUG_FUNC, "%s", "<<< cmd_setpwent");
3831 return PR_DECLINED(cmd);
3832 }
3833
sql_auth_getpwent(cmd_rec * cmd)3834 MODRET sql_auth_getpwent(cmd_rec *cmd) {
3835 struct passwd *pw;
3836 modret_t *mr;
3837
3838 if (!SQL_USERSET ||
3839 !(cmap.engine & SQL_ENGINE_FL_AUTH)) {
3840 return PR_DECLINED(cmd);
3841 }
3842
3843 sql_log(DEBUG_FUNC, "%s", ">>> cmd_getpwent");
3844
3845 /* make sure our passwd cache is complete */
3846 if (!cmap.passwd_cache_filled) {
3847 mr = sql_auth_setpwent(cmd);
3848 if (mr->data == NULL) {
3849 /* something didn't work in the setpwent call */
3850 sql_log(DEBUG_FUNC, "%s", "<<< cmd_getpwent");
3851 return PR_DECLINED(cmd);
3852 }
3853 }
3854
3855 if (cmap.curr_passwd != NULL) {
3856 pw = (struct passwd *) cmap.curr_passwd->data;
3857 cmap.curr_passwd = cmap.curr_passwd->list_next;
3858
3859 } else {
3860 pw = NULL;
3861 }
3862
3863 sql_log(DEBUG_FUNC, "%s", "<<< cmd_getpwent");
3864
3865 if (pw == NULL ||
3866 pw->pw_uid == (uid_t) -1)
3867 return PR_DECLINED(cmd);
3868
3869 return mod_create_data(cmd, (void *) pw);
3870 }
3871
sql_auth_endpwent(cmd_rec * cmd)3872 MODRET sql_auth_endpwent(cmd_rec *cmd) {
3873 if (!SQL_USERSET ||
3874 !(cmap.engine & SQL_ENGINE_FL_AUTH)) {
3875 return PR_DECLINED(cmd);
3876 }
3877
3878 sql_log(DEBUG_FUNC, "%s", ">>> cmd_endpwent");
3879
3880 cmap.curr_passwd = NULL;
3881
3882 sql_log(DEBUG_FUNC, "%s", "<<< cmd_endpwent");
3883 return PR_DECLINED(cmd);
3884 }
3885
sql_auth_setgrent(cmd_rec * cmd)3886 MODRET sql_auth_setgrent(cmd_rec *cmd) {
3887 modret_t *mr = NULL;
3888 sql_data_t *sd = NULL;
3889 unsigned long cnt = 0;
3890 struct group lgr;
3891 gid_t gid;
3892 char *groupname = NULL;
3893 char *grp_mem = NULL;
3894 char *where = NULL;
3895 array_header *ah =NULL;
3896 char *iterator = NULL;
3897 char *member = NULL;
3898
3899 if (!SQL_GROUPSET ||
3900 !(cmap.engine & SQL_ENGINE_FL_AUTH)) {
3901 return PR_DECLINED(cmd);
3902 }
3903
3904 sql_log(DEBUG_FUNC, "%s", ">>> cmd_setgrent");
3905
3906 /* if we've already filled the passwd group, just reset curr_group */
3907 if (cmap.group_cache_filled) {
3908 cmap.curr_group = group_name_cache->head;
3909 sql_log(DEBUG_FUNC, "%s", "<<< cmd_setgrent");
3910 return PR_DECLINED(cmd);
3911 }
3912
3913 if (SQL_FASTGROUPS) {
3914 /* retrieve our list of groups */
3915
3916 if (!cmap.groupcustomgroupsetfast) {
3917 where = sql_prepare_where(0, cmd, 1, cmap.groupwhere, NULL);
3918
3919 mr = sql_dispatch(sql_make_cmd(cmd->tmp_pool, 5, MOD_SQL_DEF_CONN_NAME,
3920 cmap.grptable, cmap.grpfields, where, "1"), "sql_select");
3921 if (check_response(mr, 0) < 0) {
3922 return mr;
3923 }
3924
3925 sd = (sql_data_t *) mr->data;
3926
3927 } else {
3928 mr = sql_lookup(sql_make_cmd(cmd->tmp_pool, 2, MOD_SQL_DEF_CONN_NAME,
3929 cmap.groupcustomgroupsetfast));
3930 if (check_response(mr, 0) < 0) {
3931 return mr;
3932 }
3933
3934 if (MODRET_HASDATA(mr)) {
3935 ah = mr->data;
3936 sd = pcalloc(cmd->tmp_pool, sizeof(sql_data_t));
3937
3938 /* Assume the query returned 3 columns per row. */
3939 sd->fnum = 3;
3940 sd->rnum = ah->nelts / 3;
3941 sd->data = (char **) ah->elts;
3942
3943 } else {
3944 sd = pcalloc(cmd->tmp_pool, sizeof(sql_data_t));
3945 sd->rnum = 0;
3946 }
3947 }
3948
3949 /* for each group, fill our array header and call _sql_addgroup */
3950
3951 for (cnt = 0; cnt < sd->rnum; cnt ++) {
3952 /* if the groupname is NULL for whatever reason, skip the row */
3953 groupname = sd->data[cnt * 3];
3954 if (groupname == NULL) {
3955 continue;
3956 }
3957
3958 gid = (gid_t) atol(sd->data[(cnt * 3) + 1]);
3959 grp_mem = sd->data[(cnt * 3) + 2];
3960
3961 ah = make_array(cmd->tmp_pool, 10, sizeof(char *));
3962 iterator = grp_mem;
3963
3964 for (member = strsep(&iterator, " ,"); member; member = strsep(&iterator, " ,")) {
3965 if (*member == '\0') {
3966 continue;
3967 }
3968
3969 *((char **) push_array(ah)) = member;
3970 }
3971
3972 _sql_addgroup(cmd, groupname, gid, ah);
3973 }
3974
3975 } else {
3976 /* Retrieve our list of groups. */
3977
3978 if (!cmap.groupcustomgroupset) {
3979 where = sql_prepare_where(0, cmd, 1, cmap.groupwhere, NULL);
3980
3981 mr = sql_dispatch(sql_make_cmd(cmd->tmp_pool, 6, MOD_SQL_DEF_CONN_NAME,
3982 cmap.grptable, cmap.grpfield, where, NULL, "DISTINCT"), "sql_select");
3983 if (check_response(mr, 0) < 0) {
3984 return mr;
3985 }
3986
3987 sd = (sql_data_t *) mr->data;
3988
3989 } else {
3990 mr = sql_lookup(sql_make_cmd(cmd->tmp_pool, 2, MOD_SQL_DEF_CONN_NAME,
3991 cmap.groupcustomgroupset));
3992 if (check_response(mr, 0) < 0) {
3993 return mr;
3994 }
3995
3996 if (MODRET_HASDATA(mr)) {
3997 ah = mr->data;
3998 sd = pcalloc(cmd->tmp_pool, sizeof(sql_data_t));
3999
4000 /* Assume the query only returned 1 column per row. */
4001 sd->fnum = 1;
4002 sd->rnum = ah->nelts;
4003 sd->data = (char **) ah->elts;
4004
4005 } else {
4006 sd = pcalloc(cmd->tmp_pool, sizeof(sql_data_t));
4007 sd->rnum = 0;
4008 }
4009 }
4010
4011 for (cnt = 0; cnt < sd->rnum; cnt++) {
4012 groupname = sd->data[cnt];
4013
4014 /* if the groupname is NULL for whatever reason, skip it */
4015 if (groupname == NULL)
4016 continue;
4017
4018 /* otherwise, add it to the cache */
4019 lgr.gr_gid = -1;
4020 lgr.gr_name = groupname;
4021
4022 sql_getgroup(cmd, &lgr);
4023 }
4024 }
4025
4026 cmap.group_cache_filled = 1;
4027 cmap.curr_group = group_name_cache->head;
4028
4029 sql_log(DEBUG_FUNC, "%s", "<<< cmd_setgrent");
4030 return PR_DECLINED(cmd);
4031 }
4032
sql_auth_getgrent(cmd_rec * cmd)4033 MODRET sql_auth_getgrent(cmd_rec *cmd) {
4034 struct group *gr;
4035 modret_t *mr;
4036
4037 if (!SQL_GROUPSET ||
4038 !(cmap.engine & SQL_ENGINE_FL_AUTH)) {
4039 return PR_DECLINED(cmd);
4040 }
4041
4042 sql_log(DEBUG_FUNC, "%s", ">>> cmd_getgrent");
4043
4044 /* make sure our group cache is complete */
4045 if (!cmap.group_cache_filled) {
4046 mr = sql_auth_setgrent(cmd);
4047 if (mr->data == NULL) {
4048 /* something didn't work in the setgrent call */
4049 sql_log(DEBUG_FUNC, "%s", "<<< cmd_getgrent");
4050 return PR_DECLINED(cmd);
4051 }
4052 }
4053
4054 if (cmap.curr_group != NULL) {
4055 gr = (struct group *) cmap.curr_group->data;
4056 cmap.curr_group = cmap.curr_group->list_next;
4057
4058 } else {
4059 gr = NULL;
4060 }
4061
4062 sql_log(DEBUG_FUNC, "%s", "<<< cmd_getgrent");
4063
4064 if (gr == NULL ||
4065 gr->gr_gid == (gid_t) -1) {
4066 return PR_DECLINED(cmd);
4067 }
4068
4069 return mod_create_data(cmd, (void *) gr);
4070 }
4071
sql_auth_endgrent(cmd_rec * cmd)4072 MODRET sql_auth_endgrent(cmd_rec *cmd) {
4073 if (!SQL_GROUPSET ||
4074 !(cmap.engine & SQL_ENGINE_FL_AUTH)) {
4075 return PR_DECLINED(cmd);
4076 }
4077
4078 sql_log(DEBUG_FUNC, "%s", ">>> cmd_endgrent");
4079
4080 cmap.curr_group = NULL;
4081
4082 sql_log(DEBUG_FUNC, "%s", "<<< cmd_endgrent");
4083 return PR_DECLINED(cmd);
4084 }
4085
sql_auth_getpwnam(cmd_rec * cmd)4086 MODRET sql_auth_getpwnam(cmd_rec *cmd) {
4087 struct passwd *pw;
4088 struct passwd lpw;
4089
4090 if (!SQL_USERS ||
4091 !(cmap.engine & SQL_ENGINE_FL_AUTH)) {
4092 return PR_DECLINED(cmd);
4093 }
4094
4095 sql_log(DEBUG_FUNC, "%s", ">>> cmd_getpwnam");
4096
4097 lpw.pw_uid = -1;
4098 lpw.pw_name = cmd->argv[0];
4099 pw = sql_getpasswd(cmd, &lpw);
4100
4101 if (pw == NULL ||
4102 pw->pw_uid == (uid_t) -1) {
4103 sql_log(DEBUG_FUNC, "%s", "<<< cmd_getpwnam");
4104 return PR_DECLINED(cmd);
4105 }
4106
4107 sql_log(DEBUG_FUNC, "%s", "<<< cmd_getpwnam");
4108 return mod_create_data(cmd, pw);
4109 }
4110
sql_auth_getpwuid(cmd_rec * cmd)4111 MODRET sql_auth_getpwuid(cmd_rec *cmd) {
4112 struct passwd *pw;
4113 struct passwd lpw;
4114
4115 if (!SQL_USERS ||
4116 !(cmap.engine & SQL_ENGINE_FL_AUTH)) {
4117 return PR_DECLINED(cmd);
4118 }
4119
4120 sql_log(DEBUG_FUNC, "%s", ">>> cmd_getpwuid");
4121
4122 lpw.pw_uid = *((uid_t *) cmd->argv[0]);
4123 lpw.pw_name = NULL;
4124 pw = sql_getpasswd(cmd, &lpw);
4125
4126 if (pw == NULL ||
4127 pw->pw_uid == (uid_t) -1) {
4128 sql_log(DEBUG_FUNC, "%s", "<<< cmd_getpwuid");
4129 return PR_DECLINED(cmd);
4130 }
4131
4132 sql_log(DEBUG_FUNC, "%s", "<<< cmd_getpwuid");
4133 return mod_create_data(cmd, pw);
4134 }
4135
sql_auth_getgrnam(cmd_rec * cmd)4136 MODRET sql_auth_getgrnam(cmd_rec *cmd) {
4137 struct group *gr;
4138 struct group lgr;
4139
4140 if (!SQL_GROUPS ||
4141 !(cmap.engine & SQL_ENGINE_FL_AUTH)) {
4142 return PR_DECLINED(cmd);
4143 }
4144
4145 sql_log(DEBUG_FUNC, "%s", ">>> cmd_getgrnam");
4146
4147 lgr.gr_gid = -1;
4148 lgr.gr_name = cmd->argv[0];
4149 gr = sql_getgroup(cmd, &lgr);
4150
4151 if (gr == NULL ||
4152 gr->gr_gid == (gid_t) -1) {
4153 sql_log(DEBUG_FUNC, "%s", "<<< cmd_getgrnam");
4154 return PR_DECLINED(cmd);
4155 }
4156
4157 sql_log(DEBUG_FUNC, "%s", "<<< cmd_getgrnam");
4158 return mod_create_data(cmd, gr);
4159 }
4160
sql_auth_getgrgid(cmd_rec * cmd)4161 MODRET sql_auth_getgrgid(cmd_rec *cmd) {
4162 struct group *gr;
4163 struct group lgr;
4164
4165 if (!SQL_GROUPS ||
4166 !(cmap.engine & SQL_ENGINE_FL_AUTH))
4167 return PR_DECLINED(cmd);
4168
4169 sql_log(DEBUG_FUNC, "%s", ">>> cmd_getgrgid");
4170
4171 lgr.gr_gid = *((gid_t *) cmd->argv[0]);
4172 lgr.gr_name = NULL;
4173 gr = sql_getgroup(cmd, &lgr);
4174
4175 if (gr == NULL ||
4176 gr->gr_gid == (gid_t) -1) {
4177 sql_log(DEBUG_FUNC, "%s", "<<< cmd_getgrgid");
4178 return PR_DECLINED(cmd);
4179 }
4180
4181 sql_log(DEBUG_FUNC, "%s", "<<< cmd_getgrgid");
4182 return mod_create_data(cmd, gr);
4183 }
4184
sql_auth_authenticate(cmd_rec * cmd)4185 MODRET sql_auth_authenticate(cmd_rec *cmd) {
4186 char *user = NULL;
4187 struct passwd lpw, *pw;
4188 modret_t *mr = NULL;
4189
4190 if (!SQL_USERS ||
4191 !(cmap.engine & SQL_ENGINE_FL_AUTH)) {
4192 return PR_DECLINED(cmd);
4193 }
4194
4195 sql_log(DEBUG_FUNC, "%s", ">>> cmd_auth");
4196
4197 user = cmd->argv[0];
4198
4199 /* escape our username */
4200 mr = sql_dispatch(sql_make_cmd(cmd->tmp_pool, 2, MOD_SQL_DEF_CONN_NAME,
4201 user), "sql_escapestring");
4202 if (check_response(mr, 0) < 0) {
4203 return mr;
4204 }
4205
4206 user = (char *) mr->data;
4207
4208 lpw.pw_uid = -1;
4209 lpw.pw_name = cmd->argv[0];
4210
4211 if ((pw = sql_getpasswd(cmd, &lpw)) &&
4212 !pr_auth_check(cmd->tmp_pool, pw->pw_passwd, cmd->argv[0],
4213 cmd->argv[1])) {
4214 sql_log(DEBUG_FUNC, "%s", "<<< cmd_auth");
4215 session.auth_mech = "mod_sql.c";
4216 return PR_HANDLED(cmd);
4217 }
4218
4219 sql_log(DEBUG_FUNC, "%s", "<<< cmd_auth");
4220 return PR_DECLINED(cmd);
4221 }
4222
sql_auth_check(cmd_rec * cmd)4223 MODRET sql_auth_check(cmd_rec *cmd) {
4224 /* Should we bother to see if the hashed password is what we have in the
4225 * database? or do we simply assume it is, and ignore the fact that we're
4226 * being passed the username, too?
4227 */
4228 array_header *ah = cmap.auth_list;
4229 int success = FALSE;
4230 modret_t *mr = NULL;
4231
4232 if (!SQL_USERS ||
4233 !(cmap.engine & SQL_ENGINE_FL_AUTH)) {
4234 return PR_DECLINED(cmd);
4235 }
4236
4237 sql_log(DEBUG_FUNC, "%s", ">>> cmd_check");
4238
4239 if (cmd->argv[0] == NULL) {
4240 sql_log(DEBUG_AUTH, "%s", "NULL hashed password");
4241
4242 } else if (cmd->argv[1] == NULL) {
4243 sql_log(DEBUG_AUTH, "%s", "NULL user name");
4244
4245 } else if (cmd->argv[2] == NULL) {
4246 sql_log(DEBUG_AUTH, "%s", "NULL clear password");
4247
4248 } else {
4249 register unsigned int i;
4250 char *ciphertext = cmd->argv[0];
4251 char *plaintext = cmd->argv[2];
4252
4253 if (ah == NULL) {
4254 sql_log(DEBUG_AUTH, "%s", "warning: no SQLAuthTypes configured");
4255 }
4256
4257 for (i = 0; ah && i < ah->nelts; i++) {
4258 struct sql_authtype_handler *sah;
4259
4260 sah = ((struct sql_authtype_handler **) ah->elts)[i];
4261 sql_log(DEBUG_AUTH, "checking password using SQLAuthType '%s'",
4262 sah->name);
4263
4264 mr = (sah->cb)(cmd, plaintext, ciphertext);
4265 if (!MODRET_ISERROR(mr)) {
4266 sql_log(DEBUG_AUTH, "'%s' SQLAuthType handler reports success",
4267 sah->name);
4268 success = 1;
4269 break;
4270
4271 } else {
4272 if (MODRET_HASMSG(mr)) {
4273 const char *err_msg;
4274
4275 err_msg = MODRET_ERRMSG(mr);
4276 sql_log(DEBUG_AUTH, "'%s' SQLAuthType handler reports failure: %s",
4277 sah->name, err_msg);
4278
4279 } else {
4280 sql_log(DEBUG_AUTH, "'%s' SQLAuthType handler reports failure",
4281 sah->name);
4282 }
4283 }
4284 }
4285 }
4286
4287 if (success) {
4288 struct passwd lpw;
4289
4290 /* This and the associated hack in sql_uid2name() are to support
4291 * UID reuse in the database -- people (for whatever reason) are
4292 * reusing UIDs/GIDs multiple times, and the displayed owner in a
4293 * LIST or NLST needs to match the current user if possible. This
4294 * depends on the fact that if we get success, the user exists in the
4295 * database (is this always true?).
4296 */
4297
4298 lpw.pw_uid = -1;
4299 lpw.pw_name = cmd->argv[1];
4300 cmap.authpasswd = sql_getpasswd(cmd, &lpw);
4301
4302 session.auth_mech = "mod_sql.c";
4303 sql_log(DEBUG_FUNC, "%s", "<<< cmd_check");
4304 return PR_HANDLED(cmd);
4305 }
4306
4307 sql_log(DEBUG_FUNC, "%s", "<<< cmd_check");
4308 return PR_DECLINED(cmd);
4309 }
4310
sql_auth_uid2name(cmd_rec * cmd)4311 MODRET sql_auth_uid2name(cmd_rec *cmd) {
4312 char *uid_name = NULL;
4313 struct passwd *pw;
4314 struct passwd lpw;
4315
4316 if (!SQL_USERS ||
4317 !(cmap.engine & SQL_ENGINE_FL_AUTH)) {
4318 return PR_DECLINED(cmd);
4319 }
4320
4321 sql_log(DEBUG_FUNC, "%s", ">>> cmd_uid2name");
4322
4323 lpw.pw_uid = *((uid_t *) cmd->argv[0]);
4324 lpw.pw_name = NULL;
4325
4326 /* check to see if we're looking up the current user */
4327 if (cmap.authpasswd &&
4328 lpw.pw_uid == cmap.authpasswd->pw_uid) {
4329 sql_log(DEBUG_INFO, "%s", "matched current user");
4330 pw = cmap.authpasswd;
4331
4332 } else {
4333 pw = sql_getpasswd(cmd, &lpw);
4334 }
4335
4336 sql_log(DEBUG_FUNC, "%s", "<<< cmd_uid2name");
4337
4338 if (pw == NULL) {
4339 return PR_DECLINED(cmd);
4340 }
4341
4342 /* In the case of a lookup of a negatively cached UID, the pw_name
4343 * member will be NULL, which causes an undesired handling by
4344 * the core code. Handle this case separately.
4345 */
4346 if (pw->pw_name) {
4347 uid_name = pw->pw_name;
4348
4349 } else {
4350 const char *uidstr = NULL;
4351
4352 uidstr = pr_uid2str(cmd->pool, *((uid_t *) cmd->argv[0]));
4353 uid_name = (char *) uidstr;
4354 }
4355
4356 return mod_create_data(cmd, uid_name);
4357 }
4358
sql_auth_gid2name(cmd_rec * cmd)4359 MODRET sql_auth_gid2name(cmd_rec *cmd) {
4360 char *gid_name = NULL;
4361 struct group *gr;
4362 struct group lgr;
4363
4364 if (!SQL_GROUPS ||
4365 !(cmap.engine & SQL_ENGINE_FL_AUTH)) {
4366 return PR_DECLINED(cmd);
4367 }
4368
4369 sql_log(DEBUG_FUNC, "%s", ">>> cmd_gid2name");
4370
4371 lgr.gr_gid = *((gid_t *) cmd->argv[0]);
4372 lgr.gr_name = NULL;
4373 gr = sql_getgroup(cmd, &lgr);
4374
4375 sql_log(DEBUG_FUNC, "%s", "<<< cmd_gid2name");
4376
4377 if (gr == NULL) {
4378 return PR_DECLINED(cmd);
4379 }
4380
4381 /* In the case of a lookup of a negatively cached GID, the gr_name
4382 * member will be NULL, which causes an undesired handling by
4383 * the core code. Handle this case separately.
4384 */
4385 if (gr->gr_name) {
4386 gid_name = gr->gr_name;
4387
4388 } else {
4389 const char *gidstr = NULL;
4390
4391 gidstr = pr_gid2str(cmd->pool, *((gid_t *) cmd->argv[0]));
4392 gid_name = (char *) gidstr;
4393 }
4394
4395 return mod_create_data(cmd, gid_name);
4396 }
4397
sql_auth_name2uid(cmd_rec * cmd)4398 MODRET sql_auth_name2uid(cmd_rec *cmd) {
4399 struct passwd *pw;
4400 struct passwd lpw;
4401
4402 if (!SQL_USERS ||
4403 !(cmap.engine & SQL_ENGINE_FL_AUTH)) {
4404 return PR_DECLINED(cmd);
4405 }
4406
4407 sql_log(DEBUG_FUNC, "%s", ">>> cmd_name2uid");
4408
4409 lpw.pw_uid = -1;
4410 lpw.pw_name = cmd->argv[0];
4411
4412 /* check to see if we're looking up the current user */
4413 if (cmap.authpasswd &&
4414 strcmp(lpw.pw_name, cmap.authpasswd->pw_name) == 0) {
4415 sql_log(DEBUG_INFO, "%s", "matched current user");
4416 pw = cmap.authpasswd;
4417
4418 } else {
4419 pw = sql_getpasswd(cmd, &lpw);
4420 }
4421
4422 if (pw == NULL ||
4423 pw->pw_uid == (uid_t) -1) {
4424 sql_log(DEBUG_FUNC, "%s", "<<< cmd_name2uid");
4425 return PR_DECLINED(cmd);
4426 }
4427
4428 sql_log(DEBUG_FUNC, "%s", "<<< cmd_name2uid");
4429 return mod_create_data(cmd, (void *) &pw->pw_uid);
4430 }
4431
sql_auth_name2gid(cmd_rec * cmd)4432 MODRET sql_auth_name2gid(cmd_rec *cmd) {
4433 struct group *gr;
4434 struct group lgr;
4435
4436 if (!SQL_GROUPS ||
4437 !(cmap.engine & SQL_ENGINE_FL_AUTH)) {
4438 return PR_DECLINED(cmd);
4439 }
4440
4441 sql_log(DEBUG_FUNC, "%s", ">>> cmd_name2gid");
4442
4443 lgr.gr_gid = -1;
4444 lgr.gr_name = cmd->argv[0];
4445 gr = sql_getgroup(cmd, &lgr);
4446
4447 if (gr == NULL ||
4448 gr->gr_gid == (gid_t) -1) {
4449 sql_log(DEBUG_FUNC, "%s", "<<< cmd_name2gid");
4450 return PR_DECLINED(cmd);
4451 }
4452
4453 sql_log(DEBUG_FUNC, "%s", "<<< cmd_name2gid");
4454 return mod_create_data(cmd, (void *) &gr->gr_gid);
4455 }
4456
sql_auth_getgroups(cmd_rec * cmd)4457 MODRET sql_auth_getgroups(cmd_rec *cmd) {
4458 int res;
4459
4460 if (!SQL_GROUPS ||
4461 !(cmap.engine & SQL_ENGINE_FL_AUTH)) {
4462 return PR_DECLINED(cmd);
4463 }
4464
4465 sql_log(DEBUG_FUNC, "%s", ">>> cmd_getgroups");
4466
4467 res = sql_getgroups(cmd);
4468 if (res < 0) {
4469 sql_log(DEBUG_FUNC, "%s", "<<< cmd_getgroups");
4470 return PR_DECLINED(cmd);
4471 }
4472
4473 sql_log(DEBUG_FUNC, "%s", "<<< cmd_getgroups");
4474 return mod_create_data(cmd, (void *) &res);
4475 }
4476
4477 /* XXX mod_ratio hacks. */
4478
sql_getstats(cmd_rec * cmd)4479 MODRET sql_getstats(cmd_rec *cmd) {
4480 modret_t *mr;
4481 char *query;
4482 sql_data_t *sd;
4483 char *usrwhere, *where;
4484
4485 sql_log(DEBUG_FUNC, "%s", ">>> cmd_getstats");
4486
4487 if (!cmap.sql_fstor) {
4488 return PR_DECLINED(cmd);
4489 }
4490
4491 usrwhere = pstrcat(cmd->tmp_pool, cmap.usrfield, " = '", _sql_realuser(cmd),
4492 "'", NULL);
4493
4494 where = sql_prepare_where(SQL_PREPARE_WHERE_FL_NO_TAGS, cmd, 2, usrwhere,
4495 sql_prepare_where(0, cmd, 1, cmap.userwhere, NULL), NULL);
4496
4497 query = pstrcat(cmd->tmp_pool, cmap.sql_fstor, ", ",
4498 cmap.sql_fretr, ", ", cmap.sql_bstor, ", ",
4499 cmap.sql_bretr, NULL);
4500
4501 mr = sql_dispatch(sql_make_cmd(cmd->tmp_pool, 4, MOD_SQL_DEF_CONN_NAME,
4502 cmap.usrtable, query, where), "sql_select");
4503 if (check_response(mr, 0) < 0) {
4504 return mr;
4505 }
4506
4507 sql_log(DEBUG_FUNC, "%s", "<<< cmd_getstats");
4508
4509 sd = mr->data;
4510
4511 if (sd->rnum == 0)
4512 return PR_ERROR(cmd);
4513
4514 return mod_create_data(cmd, sd->data);
4515 }
4516
sql_getratio(cmd_rec * cmd)4517 MODRET sql_getratio(cmd_rec *cmd) {
4518 modret_t *mr;
4519 char *query;
4520 sql_data_t *sd;
4521 char *usrwhere, *where;
4522
4523 if (!cmap.sql_frate) {
4524 return PR_DECLINED(cmd);
4525 }
4526
4527 sql_log(DEBUG_FUNC, "%s", ">>> cmd_getratio");
4528
4529 usrwhere = pstrcat(cmd->tmp_pool, cmap.usrfield, " = '", _sql_realuser(cmd),
4530 "'", NULL);
4531
4532 where = sql_prepare_where(SQL_PREPARE_WHERE_FL_NO_TAGS, cmd, 2, usrwhere,
4533 sql_prepare_where(0, cmd, 1, cmap.userwhere, NULL), NULL);
4534
4535 query = pstrcat(cmd->tmp_pool, cmap.sql_frate, ", ",
4536 cmap.sql_fcred, ", ", cmap.sql_brate, ", ",
4537 cmap.sql_bcred, NULL);
4538
4539 mr = sql_dispatch(sql_make_cmd(cmd->tmp_pool, 4, MOD_SQL_DEF_CONN_NAME,
4540 cmap.usrtable, query, where), "sql_select");
4541 if (check_response(mr, 0) < 0)
4542 return mr;
4543
4544 sql_log(DEBUG_FUNC, "%s", "<<< cmd_getratio");
4545
4546 sd = mr->data;
4547
4548 if (sd->rnum == 0)
4549 return PR_ERROR(cmd);
4550
4551 return mod_create_data(cmd, sd->data);
4552 }
4553
4554 /*****************************************************************
4555 *
4556 * CONFIGURATION DIRECTIVE HANDLERS
4557 *
4558 *****************************************************************/
4559
set_sqlratiostats(cmd_rec * cmd)4560 MODRET set_sqlratiostats(cmd_rec * cmd)
4561 {
4562 int b;
4563
4564 CHECK_CONF(cmd, CONF_ROOT | CONF_GLOBAL);
4565
4566 switch (cmd->argc - 1) {
4567 default:
4568 CONF_ERROR(cmd, "requires a boolean or 4 field names: "
4569 "fstor fretr bstor bretr");
4570 case 1:
4571 if ((b = get_boolean(cmd, 1)) == -1)
4572 CONF_ERROR(cmd, "requires a boolean or 4 field names: "
4573 "fstor fretr bstor bretr");
4574 if (b)
4575 add_config_param_str("SQLRatioStats", 4,
4576 "fstor", "fretr", "bstor", "bretr");
4577 break;
4578
4579 case 4:
4580 add_config_param_str("SQLRatioStats", 4,
4581 (void *) cmd->argv[1], (void *) cmd->argv[2],
4582 (void *) cmd->argv[3], (void *) cmd->argv[4]);
4583 }
4584
4585 return PR_HANDLED(cmd);
4586 }
4587
set_sqlnegativecache(cmd_rec * cmd)4588 MODRET set_sqlnegativecache(cmd_rec *cmd) {
4589 int bool = -1;
4590 config_rec *c = NULL;
4591
4592 CHECK_ARGS(cmd, 1);
4593 CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
4594
4595 bool = get_boolean(cmd, 1);
4596 if (bool == -1)
4597 CONF_ERROR(cmd, "expected a Boolean parameter");
4598
4599 c = add_config_param(cmd->argv[0], 1, NULL);
4600 c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
4601 *((unsigned char *) c->argv[0]) = bool;
4602
4603 return PR_HANDLED(cmd);
4604 }
4605
4606 /* usage: SQLOptions opt1 [opt2 ...] */
set_sqloptions(cmd_rec * cmd)4607 MODRET set_sqloptions(cmd_rec *cmd) {
4608 config_rec *c;
4609 unsigned long opts = 0UL;
4610 register unsigned int i;
4611
4612 if (cmd->argc-1 == 0)
4613 CONF_ERROR(cmd, "wrong number of parameters");
4614
4615 CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
4616
4617 c = add_config_param(cmd->argv[0], 1, NULL);
4618
4619 for (i = 1; i < cmd->argc; i++) {
4620 if (strcasecmp(cmd->argv[i], "NoDisconnectOnError") == 0) {
4621 opts |= SQL_OPT_NO_DISCONNECT_ON_ERROR;
4622
4623 } else if (strcasecmp(cmd->argv[i], "UseNormalizedGroupSchema") == 0) {
4624 opts |= SQL_OPT_USE_NORMALIZED_GROUP_SCHEMA;
4625
4626 } else if (strcasecmp(cmd->argv[i], "NoReconnect") == 0) {
4627 opts |= SQL_OPT_NO_RECONNECT;
4628
4629 } else if (strcasecmp(cmd->argv[i], "IgnoreConfigFile") == 0) {
4630 opts |= SQL_OPT_IGNORE_CONFIG_FILE;
4631
4632 } else {
4633 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unknown SQLOption '",
4634 cmd->argv[i], "'", NULL));
4635 }
4636 }
4637
4638 c->argv[0] = pcalloc(c->pool, sizeof(unsigned long));
4639 *((unsigned long *) c->argv[0]) = opts;
4640
4641 return PR_HANDLED(cmd);
4642 }
4643
set_sqlratios(cmd_rec * cmd)4644 MODRET set_sqlratios(cmd_rec * cmd)
4645 {
4646 int b;
4647
4648 CHECK_CONF(cmd, CONF_ROOT | CONF_GLOBAL);
4649
4650 switch (cmd->argc - 1) {
4651 default:
4652 CONF_ERROR(cmd, "requires a boolean or 4 field names: "
4653 "frate fcred brate bcred");
4654 case 1:
4655 if ((b = get_boolean(cmd, 1)) == -1)
4656 CONF_ERROR(cmd, "requires a boolean or 4 field names: "
4657 "frate fcred brate bcred");
4658 if (b)
4659 add_config_param_str("SQLRatios", 4,
4660 "frate", "fcred", "brate", "bcred");
4661 break;
4662
4663 case 4:
4664 add_config_param_str("SQLRatios", 4,
4665 (void *) cmd->argv[1], (void *) cmd->argv[2],
4666 (void *) cmd->argv[3], (void *) cmd->argv[4]);
4667 }
4668
4669 return PR_HANDLED(cmd);
4670 }
4671
add_virtualstr(char * name,cmd_rec * cmd)4672 MODRET add_virtualstr(char *name, cmd_rec *cmd) {
4673 CHECK_ARGS(cmd, 1);
4674 CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL);
4675
4676 add_config_param_str(name, 1, (void *) cmd->argv[1]);
4677 return PR_HANDLED(cmd);
4678 }
4679
4680 /* usage: SQLUserInfo table(s) usernamefield passwdfield UID GID homedir
4681 * shell | custom:/<sql-named-query>[/<sql-named-query>[/<sql-named-query>[/<sql-named-query>]]]
4682 */
set_sqluserinfo(cmd_rec * cmd)4683 MODRET set_sqluserinfo(cmd_rec *cmd) {
4684
4685 if (cmd->argc-1 != 1 &&
4686 cmd->argc-1 != 7) {
4687 CONF_ERROR(cmd, "missing parameters");
4688 }
4689
4690 CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL);
4691
4692 if (cmd->argc-1 == 1) {
4693 char *user = NULL, *userbyid = NULL, *userset = NULL, *usersetfast = NULL;
4694 char *param, *ptr = NULL;
4695
4696 /* If only one parameter is used, it must be of the "custom:/" form. */
4697 param = cmd->argv[1];
4698 if (strncmp("custom:/", param, 8) != 0) {
4699 CONF_ERROR(cmd, "badly formatted parameter");
4700 }
4701
4702 ptr = strchr(param + 8, '/');
4703 if (ptr == NULL) {
4704 add_config_param_str("SQLCustomUserInfoByName", 1, param + 8);
4705 return PR_HANDLED(cmd);
4706 }
4707
4708 *ptr = '\0';
4709 user = param + 8;
4710 userbyid = ptr + 1;
4711
4712 add_config_param_str("SQLCustomUserInfoByName", 1, user);
4713
4714 ptr = strchr(userbyid, '/');
4715 if (ptr == NULL) {
4716 add_config_param_str("SQLCustomUserInfoByID", 1, userbyid);
4717 return PR_HANDLED(cmd);
4718 }
4719
4720 *ptr = '\0';
4721 userset = ptr + 1;
4722
4723 add_config_param_str("SQLCustomUserInfoByID", 1, userbyid);
4724
4725 ptr = strchr(userset, '/');
4726 if (ptr == NULL) {
4727 add_config_param_str("SQLCustomUserInfoAllNames", 1, userset);
4728 return PR_HANDLED(cmd);
4729 }
4730
4731 *ptr = '\0';
4732 usersetfast = ptr + 1;
4733
4734 add_config_param_str("SQLCustomUserInfoAllNames", 1, userset);
4735 add_config_param_str("SQLCustomUserInfoAllUsers", 1, usersetfast);
4736 return PR_HANDLED(cmd);
4737 }
4738
4739 /* required to exist - not even going to check them. */
4740 add_config_param_str("SQLUserTable", 1, (void *) cmd->argv[1]);
4741 add_config_param_str("SQLUsernameField", 1, (void *) cmd->argv[2]);
4742 add_config_param_str("SQLPasswordField", 1, (void *) cmd->argv[3]);
4743
4744 /* These could be "NULL" */
4745 if (strncasecmp("null", cmd->argv[4], 5) != 0)
4746 add_config_param_str("SQLUidField", 1, (void *) cmd->argv[4]);
4747
4748 if (strncasecmp("null", cmd->argv[5], 5) != 0)
4749 add_config_param_str("SQLGidField", 1, (void *) cmd->argv[5]);
4750
4751 if (strncasecmp("null", cmd->argv[6], 5) != 0)
4752 add_config_param_str("SQLHomedirField", 1, (void *) cmd->argv[6]);
4753
4754 if (strncasecmp("null", cmd->argv[7], 5) != 0)
4755 add_config_param_str("SQLShellField", 1, (void *) cmd->argv[7]);
4756
4757 return PR_HANDLED(cmd);
4758 }
4759
4760 /* usage: SQLUserPrimaryKey column-name */
set_sqluserprimarykey(cmd_rec * cmd)4761 MODRET set_sqluserprimarykey(cmd_rec *cmd) {
4762 return add_virtualstr(cmd->argv[0], cmd);
4763 }
4764
set_sqluserwhereclause(cmd_rec * cmd)4765 MODRET set_sqluserwhereclause(cmd_rec *cmd) {
4766 return add_virtualstr(cmd->argv[0], cmd);
4767 }
4768
4769 /* usage: SQLGroupInfo table(s) groupnamefield gidfield membersfield */
4770 /* usage: SQLGroupInfo table(s) groupnamefield gidfield membersfield |
4771 * custom:/<sql-named-query>/<sql-named-query>/sql-named-query[/<sql-named-query[/<sql-named-query>]]
4772 */
set_sqlgroupinfo(cmd_rec * cmd)4773 MODRET set_sqlgroupinfo(cmd_rec *cmd) {
4774
4775 if (cmd->argc-1 != 1 &&
4776 cmd->argc-1 != 4) {
4777 CONF_ERROR(cmd, "missing parameters");
4778 }
4779
4780 CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL);
4781
4782 if (cmd->argc-1 == 1) {
4783 char *groupbyname = NULL, *groupbyid = NULL, *groupmembers = NULL,
4784 *groupset = NULL, *groupsetfast = NULL;
4785 char *param, *ptr = NULL;
4786
4787 /* If only one parameter is used, it must be of the "custom:/" form. */
4788 param = cmd->argv[1];
4789 if (strncmp("custom:/", param, 8) != 0) {
4790 CONF_ERROR(cmd, "badly formatted parameter");
4791 }
4792
4793 ptr = strchr(param + 8, '/');
4794 if (ptr == NULL) {
4795 CONF_ERROR(cmd, "badly formatted parameter");
4796 }
4797
4798 *ptr = '\0';
4799 groupbyname = param + 8;
4800 groupbyid = ptr + 1;
4801
4802 add_config_param_str("SQLCustomGroupInfoByName", 1, groupbyname);
4803
4804 ptr = strchr(groupbyid, '/');
4805 if (ptr == NULL) {
4806 CONF_ERROR(cmd, "badly formatted parameter");
4807 }
4808
4809 *ptr = '\0';
4810 groupmembers = ptr + 1;
4811
4812 add_config_param_str("SQLCustomGroupInfoByID", 1, groupbyid);
4813
4814 ptr = strchr(groupmembers, '/');
4815 if (ptr == NULL) {
4816 add_config_param_str("SQLCustomGroupInfoMembers", 1, groupmembers);
4817 return PR_HANDLED(cmd);
4818 }
4819
4820 *ptr = '\0';
4821 groupset = ptr + 1;
4822
4823 add_config_param_str("SQLCustomGroupInfoMembers", 1, groupmembers);
4824
4825 ptr = strchr(groupset, '/');
4826 if (ptr == NULL) {
4827 add_config_param_str("SQLCustomGroupInfoAllNames", 1, groupset);
4828 return PR_HANDLED(cmd);
4829 }
4830
4831 *ptr = '\0';
4832 groupsetfast = ptr + 1;
4833
4834 add_config_param_str("SQLCustomGroupInfoAllNames", 1, groupset);
4835 add_config_param_str("SQLCustomGroupInfoAllGroups", 1, groupsetfast);
4836 return PR_HANDLED(cmd);
4837 }
4838
4839 /* required to exist - not even going to check them. */
4840 add_config_param_str("SQLGroupTable", 1, cmd->argv[1]);
4841 add_config_param_str("SQLGroupnameField", 1, cmd->argv[2]);
4842 add_config_param_str("SQLGroupGIDField", 1, cmd->argv[3]);
4843 add_config_param_str("SQLGroupMembersField", 1, cmd->argv[4]);
4844
4845 return PR_HANDLED(cmd);
4846 }
4847
4848 /* usage: SQLGroupPrimaryKey column-name */
set_sqlgroupprimarykey(cmd_rec * cmd)4849 MODRET set_sqlgroupprimarykey(cmd_rec *cmd) {
4850 return add_virtualstr(cmd->argv[0], cmd);
4851 }
4852
set_sqlgroupwhereclause(cmd_rec * cmd)4853 MODRET set_sqlgroupwhereclause(cmd_rec *cmd) {
4854 return add_virtualstr(cmd->argv[0], cmd);
4855 }
4856
set_sqldefaulthomedir(cmd_rec * cmd)4857 MODRET set_sqldefaulthomedir(cmd_rec *cmd) {
4858 return add_virtualstr("SQLDefaultHomedir", cmd);
4859 }
4860
4861 /* usage: SQLKeepAlive interval [select-stmt] */
set_sqlkeepalive(cmd_rec * cmd)4862 MODRET set_sqlkeepalive(cmd_rec *cmd) {
4863 config_rec *c;
4864 int interval;
4865 const char *stmt;
4866
4867 if (cmd->argc != 2 &&
4868 cmd->argc != 3) {
4869 CONF_ERROR(cmd, "wrong number of parameters");
4870 }
4871
4872 CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL);
4873
4874 interval = atoi(cmd->argv[1]);
4875 if (interval < 0) {
4876 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "interval '", (char *) cmd->argv[1],
4877 "' must be equal to or greater than zero", NULL));
4878 }
4879
4880 /* Default SQL query to use: `SELECT 1` */
4881 stmt = "SELECT 1";
4882 if (cmd->argc == 3) {
4883 stmt = cmd->argv[2];
4884 }
4885
4886 c = add_config_param(cmd->argv[0], 2, NULL, NULL);
4887 c->argv[0] = pcalloc(c->pool, sizeof(int));
4888 *((int *) c->argv[0]) = interval;
4889 c->argv[1] = pstrdup(c->pool, stmt);
4890
4891 return PR_HANDLED(cmd);
4892 }
4893
4894 /* usage: SQLLog cmdlist query-name ["IGNORE_ERRORS"] */
set_sqllog(cmd_rec * cmd)4895 MODRET set_sqllog(cmd_rec *cmd) {
4896 config_rec *c;
4897 char *name, *namep;
4898 char *cmds;
4899 char *iterator;
4900
4901 CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL);
4902
4903 if (cmd->argc < 3 ||
4904 cmd->argc > 4) {
4905 CONF_ERROR(cmd, "expected cmdlist query-name [IGNORE_ERRORS]");
4906 }
4907
4908 /* For each element in the command list, add a 'SQLLog_CMD' config_rec..
4909 * this is an optimization that speeds up logging and also simplifies the
4910 * logging code, since there's no need to run through and parse a bunch
4911 * of potenitally unused SQLLog statements each time any command is run.
4912 */
4913
4914 cmds = cmd->argv[1];
4915 iterator = cmds;
4916
4917 for (name = strsep(&iterator, ", "); name; name = strsep(&iterator, ", ")) {
4918 if (*name == '\0')
4919 continue;
4920 for (namep = name; *namep != '\0'; namep++)
4921 *namep = toupper(*namep);
4922
4923 name = pstrcat(cmd->tmp_pool, "SQLLog_", name, NULL);
4924 if (cmd->argc == 4 &&
4925 strcasecmp(cmd->argv[3], "IGNORE_ERRORS") == 0) {
4926 c = add_config_param_str(name, 2, cmd->argv[2], "ignore");
4927
4928 } else {
4929 c = add_config_param_str(name, 1, cmd->argv[2]);
4930 }
4931
4932 if (pr_module_exists("mod_ifsession.c")) {
4933 /* If the mod_ifsession module is in use, then we need to set the
4934 * CF_MULTI flag, so that SQLLog directives that appear in
4935 * mod_ifsession's <IfClass>/<IfGroup>/<IfUser> sections work
4936 * properly.
4937 */
4938 c->flags |= CF_MULTI;
4939 }
4940 }
4941
4942 return PR_HANDLED(cmd);
4943 }
4944
4945 /* usage: SQLLogFile path */
set_sqllogfile(cmd_rec * cmd)4946 MODRET set_sqllogfile(cmd_rec *cmd) {
4947 CHECK_ARGS(cmd, 1);
4948 CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
4949
4950 add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
4951 return PR_HANDLED(cmd);
4952 }
4953
4954 /* usage: SQLLogOnEvent event query-name ["IGNORE_ERRORS"] */
set_sqllogonevent(cmd_rec * cmd)4955 MODRET set_sqllogonevent(cmd_rec *cmd) {
4956 config_rec *c;
4957 char *event_name, *config_name;
4958
4959 CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL);
4960
4961 if (cmd->argc < 3 ||
4962 cmd->argc > 4) {
4963 CONF_ERROR(cmd, "expected event query-name [IGNORE_ERRORS]");
4964 }
4965
4966 /* Provide convenience aliases, falling back to "raw" event name. */
4967 if (strcasecmp(cmd->argv[1], "MaxClientsPerClass") == 0) {
4968 event_name = "mod_auth.max-clients-per-class";
4969
4970 } else if (strcasecmp(cmd->argv[1], "MaxClientsPerHost") == 0) {
4971 event_name = "mod_auth.max-clients-per-host";
4972
4973 } else if (strcasecmp(cmd->argv[1], "MaxClientsPerUser") == 0) {
4974 event_name = "mod_auth.max-clients-per-user";
4975
4976 } else if (strcasecmp(cmd->argv[1], "MaxCommandRate") == 0) {
4977 event_name = "core.max-command-rate";
4978
4979 } else if (strcasecmp(cmd->argv[1], "MaxConnectionsPerHost") == 0) {
4980 event_name = "mod_auth.max-connections-per-host";
4981
4982 } else if (strcasecmp(cmd->argv[1], "MaxHostsPerUser") == 0) {
4983 event_name = "mod_auth.max-hosts-per-user";
4984
4985 } else if (strcasecmp(cmd->argv[1], "MaxLoginAttempts") == 0) {
4986 event_name = "mod_auth.max-login-attempts";
4987
4988 } else if (strcasecmp(cmd->argv[1], "RootLogin") == 0) {
4989 event_name = "mod_auth.root-login";
4990
4991 } else if (strcasecmp(cmd->argv[1], "TimeoutIdle") == 0) {
4992 event_name = "core.timeout-idle";
4993
4994 } else if (strcasecmp(cmd->argv[1], "TimeoutLogin") == 0) {
4995 event_name = "core.timeout-login";
4996
4997 } else if (strcasecmp(cmd->argv[1], "TimeoutNoTransfer") == 0) {
4998 event_name = "core.timeout-no-transfer";
4999
5000 } else if (strcasecmp(cmd->argv[1], "TimeoutStalled") == 0) {
5001 event_name = "core.timeout-stalled";
5002
5003 } else if (strcasecmp(cmd->argv[1], "UserBanned") == 0) {
5004 event_name = "mod_ban.ban-user";
5005
5006 } else if (strcasecmp(cmd->argv[1], "HostBanned") == 0) {
5007 event_name = "mod_ban.ban-host";
5008
5009 } else if (strcasecmp(cmd->argv[1], "ClassBanned") == 0) {
5010 event_name = "mod_ban.ban-class";
5011
5012 } else {
5013 event_name = cmd->argv[1];
5014 }
5015
5016 /* Add a 'SQLLog_Event_<event>' config_rec. This is an optimization that
5017 * speeds up logging and also simplifies the logging code.
5018 */
5019 config_name = pstrcat(cmd->tmp_pool, "SQLLog_Event_", event_name, NULL);
5020 if (cmd->argc == 4 &&
5021 strcasecmp(cmd->argv[3], "IGNORE_ERRORS") == 0) {
5022 c = add_config_param_str(config_name, 3, cmd->argv[2], event_name,
5023 "ignore");
5024
5025 } else {
5026 c = add_config_param_str(config_name, 2, cmd->argv[2], event_name);
5027 }
5028
5029 if (pr_module_exists("mod_ifsession.c")) {
5030 /* If the mod_ifsession module is in use, then we need to set the
5031 * CF_MULTI flag, so that SQLLogOnEvent directives that appear in
5032 * mod_ifsession's <IfClass>/<IfGroup>/<IfUser> sections work
5033 * properly.
5034 */
5035 c->flags |= CF_MULTI;
5036 }
5037
5038 /* In addition, we also need to set a SQLLogOnEvent config_rec, for
5039 * lookup in sess_init(), so that we know to which events to subscribe.
5040 */
5041 add_config_param_str(cmd->argv[0], 1, event_name);
5042
5043 return PR_HANDLED(cmd);
5044 }
5045
5046 /* usage: SQLNamedConnectInfo name backend info [user [pass [ttl]]]
5047 * [ssl-cert:<path>] [ssl-key:<path>] [ssl-ca:/path] [ssl-ciphers:str]
5048 */
set_sqlnamedconnectinfo(cmd_rec * cmd)5049 MODRET set_sqlnamedconnectinfo(cmd_rec *cmd) {
5050 register unsigned int i;
5051 int argc = 0;
5052 char *conn_name = NULL;
5053 char *backend = NULL;
5054 char **argv = NULL, *info = NULL, *user = "", *pass = "", *ttl = NULL;
5055 char *ssl_cert_file = NULL, *ssl_key_file = NULL, *ssl_ca_file = NULL;
5056 char *ssl_ca_dir = NULL, *ssl_ciphers = NULL;
5057 struct sql_backend *sb;
5058 array_header *params;
5059
5060 CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL);
5061
5062 if (cmd->argc-1 < 3 ||
5063 cmd->argc-1 > 10) {
5064 CONF_ERROR(cmd, "requires 3 to 10 parameters; check the mod_sql docs");
5065 }
5066
5067 /* First, deal with any required parameters. */
5068 conn_name = cmd->argv[1];
5069
5070 backend = cmd->argv[2];
5071 sb = sql_get_backend(backend);
5072 if (sb == NULL) {
5073 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, ": SQLBackend '", backend,
5074 "' not supported", NULL));
5075 }
5076
5077 /* Next, search for/process any optional named parameters. */
5078 params = make_array(cmd->tmp_pool, 0, sizeof(char *));
5079
5080 for (i = 3; i < cmd->argc; i++) {
5081 if (strncmp(cmd->argv[i], "ssl-cert:", 9) == 0) {
5082 char *path;
5083
5084 path = cmd->argv[i];
5085
5086 /* Advance past the "ssl-cert:" prefix. */
5087 path += 9;
5088
5089 /* Check the file exists! */
5090 if (file_exists2(cmd->tmp_pool, path) == TRUE) {
5091 ssl_cert_file = path;
5092
5093 } else {
5094 pr_log_pri(PR_LOG_NOTICE, MOD_SQL_VERSION
5095 ": %s: SSL certificate '%s': %s", (char *) cmd->argv[0], path,
5096 strerror(ENOENT));
5097 }
5098
5099 } else if (strncmp(cmd->argv[i], "ssl-key:", 8) == 0) {
5100 char *path;
5101
5102 path = cmd->argv[i];
5103
5104 /* Advance past the "ssl-key:" prefix. */
5105 path += 8;
5106
5107 /* Check the file exists! */
5108 if (file_exists2(cmd->tmp_pool, path) == TRUE) {
5109 ssl_key_file = path;
5110
5111 } else {
5112 pr_log_pri(PR_LOG_NOTICE, MOD_SQL_VERSION
5113 ": %s: SSL certificate key '%s': %s", (char *) cmd->argv[0], path,
5114 strerror(ENOENT));
5115 }
5116
5117 } else if (strncmp(cmd->argv[i], "ssl-ca:", 7) == 0) {
5118 char *path;
5119
5120 path = cmd->argv[i];
5121
5122 /* Advance past the "ssl-ca:" prefix. */
5123 path += 7;
5124
5125 /* Check the file exists! */
5126 if (file_exists2(cmd->tmp_pool, path) == TRUE) {
5127 ssl_ca_file = path;
5128
5129 } else if (dir_exists2(cmd->tmp_pool, path) == TRUE) {
5130 ssl_ca_dir = path;
5131
5132 } else {
5133 pr_log_pri(PR_LOG_NOTICE, MOD_SQL_VERSION
5134 ": %s: SSL CA '%s': %s", (char *) cmd->argv[0], path,
5135 strerror(ENOENT));
5136 }
5137
5138 } else if (strncmp(cmd->argv[i], "ssl-ciphers:", 12) == 0) {
5139 char *ciphers;
5140
5141 ciphers = cmd->argv[i];
5142
5143 /* Advance past the "ssl-ciphers:" prefix. */
5144 ciphers += 12;
5145
5146 ssl_ciphers = ciphers;
5147
5148 } else {
5149 *((char **) push_array(params)) = cmd->argv[i];
5150 }
5151 }
5152
5153 /* Last, handle any optional positional parameters. */
5154
5155 argc = params->nelts;
5156 argv = params->elts;
5157
5158 if (argc >= 1) {
5159 info = argv[0];
5160 }
5161
5162 if (argc >= 2) {
5163 user = argv[1];
5164 }
5165
5166 if (argc >= 3) {
5167 pass = argv[2];
5168 }
5169
5170 /* Note: The only connection policy which is honored for NamedConnInfos
5171 * is the TTL policy, i.e. for setting a timer on this connect. Other
5172 * policies are only honored if set on the default connection via
5173 * SQLConnectInfo.
5174 */
5175
5176 if (argc >= 4) {
5177 ttl = argv[3];
5178
5179 } else {
5180 ttl = "0";
5181 }
5182
5183 (void) add_config_param_str(cmd->argv[0], 11, conn_name, backend, info, user,
5184 pass, ttl, ssl_cert_file, ssl_key_file, ssl_ca_file, ssl_ca_dir,
5185 ssl_ciphers);
5186 return PR_HANDLED(cmd);
5187 }
5188
5189 /* Parse the provided SQL statement for any LogFormat variables; the output
5190 * as a parsed buffer which will be resolved at runtime.
5191 */
parse_named_query(pool * p,const char * stmt_text,unsigned char * stmt_buf,size_t stmt_bufsz,size_t * stmt_buflen,int flags)5192 static int parse_named_query(pool *p, const char *stmt_text,
5193 unsigned char *stmt_buf, size_t stmt_bufsz, size_t *stmt_buflen,
5194 int flags) {
5195 int res;
5196 pool *tmp_pool;
5197 pr_jot_ctx_t *jot_ctx;
5198 pr_jot_parsed_t *jot_parsed;
5199
5200 tmp_pool = make_sub_pool(p);
5201 jot_ctx = pcalloc(tmp_pool, sizeof(pr_jot_ctx_t));
5202 jot_parsed = pcalloc(tmp_pool, sizeof(pr_jot_parsed_t));
5203 jot_parsed->bufsz = jot_parsed->buflen = stmt_bufsz;
5204 jot_parsed->ptr = jot_parsed->buf = stmt_buf;
5205
5206 jot_ctx->log = jot_parsed;
5207
5208 res = pr_jot_parse_logfmt(tmp_pool, stmt_text, jot_ctx, pr_jot_parse_on_meta,
5209 pr_jot_parse_on_unknown, pr_jot_parse_on_other, flags);
5210 if (res < 0) {
5211 pr_log_pri(PR_LOG_NOTICE, MOD_SQL_VERSION
5212 ": error parsing SQLNamedQuery '%s': %s", stmt_text, strerror(errno));
5213 }
5214
5215 *stmt_buflen = jot_parsed->bufsz - jot_parsed->buflen;
5216 stmt_buf[*stmt_buflen] = '\0';
5217
5218 destroy_pool(tmp_pool);
5219 return res;
5220 }
5221
5222 /* usage: SQLNamedQuery name type query-string [table-name] [conn-name] */
set_sqlnamedquery(cmd_rec * cmd)5223 MODRET set_sqlnamedquery(cmd_rec *cmd) {
5224 int res;
5225 config_rec *c = NULL;
5226 char *name = NULL;
5227 unsigned char stmt_buf[4096];
5228 size_t stmt_buflen;
5229
5230 CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL);
5231
5232 if (cmd->argc < 4) {
5233 CONF_ERROR(cmd, "requires at least 3 parameters");
5234 }
5235
5236 res = parse_named_query(cmd->tmp_pool, cmd->argv[3], stmt_buf,
5237 sizeof(stmt_buf)-1, &stmt_buflen, PR_JOT_LOGFMT_PARSE_FL_UNKNOWN_AS_CUSTOM);
5238 if (res < 0) {
5239 CONF_ERROR(cmd, "syntax error in query");
5240 }
5241
5242 name = pstrcat(cmd->tmp_pool, "SQLNamedQuery_", cmd->argv[1], NULL);
5243
5244 if (strcasecmp(cmd->argv[2], "SELECT") == 0) {
5245 char *conn_name;
5246
5247 conn_name = MOD_SQL_DEF_CONN_NAME;
5248 if (cmd->argc == 5) {
5249 conn_name = cmd->argv[4];
5250 }
5251
5252 c = add_config_param_str(name, 3, SQL_SELECT_C, stmt_buf, conn_name);
5253
5254 } else if (strcasecmp(cmd->argv[2], "FREEFORM") == 0) {
5255 char *conn_name;
5256
5257 conn_name = MOD_SQL_DEF_CONN_NAME;
5258 if (cmd->argc == 5) {
5259 conn_name = cmd->argv[4];
5260 }
5261
5262 c = add_config_param_str(name, 3, SQL_FREEFORM_C, stmt_buf, conn_name);
5263
5264 } else if (strcasecmp(cmd->argv[2], "INSERT") == 0) {
5265 char *conn_name;
5266
5267 if (cmd->argc < 5) {
5268 CONF_ERROR(cmd, "expected 'INSERT' query-string table-name");
5269 }
5270
5271 conn_name = MOD_SQL_DEF_CONN_NAME;
5272 if (cmd->argc == 6) {
5273 conn_name = cmd->argv[5];
5274 }
5275
5276 c = add_config_param_str(name, 4, SQL_INSERT_C, stmt_buf, cmd->argv[4],
5277 conn_name);
5278
5279 } else if (strcasecmp(cmd->argv[2], "UPDATE") == 0) {
5280 char *conn_name;
5281
5282 if (cmd->argc < 5) {
5283 CONF_ERROR(cmd, "expected 'UPDATE' query-string table-name");
5284 }
5285
5286 conn_name = MOD_SQL_DEF_CONN_NAME;
5287 if (cmd->argc == 6) {
5288 conn_name = cmd->argv[5];
5289 }
5290
5291 c = add_config_param_str(name, 4, SQL_UPDATE_C, stmt_buf, cmd->argv[4],
5292 conn_name);
5293
5294 } else {
5295 CONF_ERROR(cmd, "type must be SELECT, INSERT, UPDATE, or FREEFORM");
5296 }
5297
5298 if (c != NULL) {
5299 c->flags |= CF_MULTI;
5300 }
5301
5302 return PR_HANDLED(cmd);
5303 }
5304
5305 /* usage: SQLShowInfo cmdlist numeric format-string */
set_sqlshowinfo(cmd_rec * cmd)5306 MODRET set_sqlshowinfo(cmd_rec *cmd) {
5307 int res;
5308 config_rec *c = NULL;
5309 char *cmds = NULL, *iterator = NULL, *name = NULL;
5310 unsigned char stmt_buf[4096];
5311 size_t stmt_buflen;
5312
5313 CHECK_ARGS(cmd, 3);
5314 CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL);
5315
5316 res = parse_named_query(cmd->tmp_pool, cmd->argv[3], stmt_buf,
5317 sizeof(stmt_buf)-1, &stmt_buflen, PR_JOT_LOGFMT_PARSE_FL_UNKNOWN_AS_CUSTOM);
5318 if (res < 0) {
5319 CONF_ERROR(cmd, "syntax error in query");
5320 }
5321
5322 cmds = pstrdup(cmd->tmp_pool, cmd->argv[1]);
5323 iterator = cmds;
5324
5325 for (name = strsep(&iterator, ", "); name; name = strsep(&iterator, ", ")) {
5326 char *ptr = NULL;
5327
5328 if (*name == '\0') {
5329 continue;
5330 }
5331
5332 for (ptr = name; *ptr != '\0'; ptr++) {
5333 *ptr = toupper(*ptr);
5334 }
5335
5336 name = pstrcat(cmd->tmp_pool, "SQLShowInfo_", name, NULL);
5337
5338 c = add_config_param_str(name, 2, cmd->argv[2], stmt_buf);
5339
5340 if (pr_module_exists("mod_ifsession.c")) {
5341 /* If the mod_ifsession module is in use, then we need to set the
5342 * CF_MULTI flag, so that SQLShowInfo directives that appear in
5343 * mod_ifsession's <IfClass>/<IfGroup>/<IfUser> sections work
5344 * properly.
5345 */
5346 c->flags |= CF_MULTI;
5347 }
5348 }
5349
5350 return PR_HANDLED(cmd);
5351 }
5352
set_sqlauthenticate(cmd_rec * cmd)5353 MODRET set_sqlauthenticate(cmd_rec *cmd) {
5354 config_rec *c = NULL;
5355 char *arg = NULL;
5356 int authmask = 0;
5357 unsigned long cnt = 0;
5358 int groupset_flag, userset_flag, groups_flag, users_flag;
5359
5360 CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL);
5361
5362 if (cmd->argc < 2 ||
5363 cmd->argc > 5) {
5364 CONF_ERROR(cmd, "requires 1 to 4 parameters; check the mod_sql docs");
5365 }
5366
5367 groupset_flag = userset_flag = groups_flag = users_flag = FALSE;
5368
5369 /* We're setting our authmask here -- we have a bunch of checks needed to
5370 * make sure users aren't trying to screw around with us.
5371 */
5372
5373 if (cmd->argc == 2 &&
5374 strcasecmp(cmd->argv[1], "on") == 0) {
5375 authmask = SQL_AUTH_GROUPSET|SQL_AUTH_USERSET|SQL_AUTH_USERS|
5376 SQL_AUTH_GROUPS;
5377
5378 } else if (!((cmd->argc == 2) && !strcasecmp(cmd->argv[1], "off"))) {
5379 for (cnt = 1; cnt < cmd->argc; cnt++) {
5380 arg = cmd->argv[cnt];
5381
5382 if (strncasecmp("groupset", arg, 8) == 0) {
5383 if (groupset_flag) {
5384 CONF_ERROR(cmd, "groupset already set");
5385 }
5386
5387 if (strcasecmp("groupsetfast", arg) == 0) {
5388 authmask |= SQL_FAST_GROUPSET;
5389
5390 } else if (strlen(arg) > 8) {
5391 CONF_ERROR(cmd, "unknown argument");
5392 }
5393
5394 authmask |= SQL_AUTH_GROUPSET;
5395 groupset_flag = TRUE;
5396
5397 } else if (strncasecmp("userset", arg, 7) == 0) {
5398 if (userset_flag) {
5399 CONF_ERROR(cmd, "userset already set");
5400 }
5401
5402 if (strcasecmp("usersetfast", arg) == 0) {
5403 authmask |= SQL_FAST_USERSET;
5404
5405 } else if (strlen(arg) > 7) {
5406 CONF_ERROR(cmd, "unknown argument");
5407 }
5408
5409 authmask |= SQL_AUTH_USERSET;
5410 userset_flag = TRUE;
5411
5412 } else if (strncasecmp("groups", arg, 6) == 0) {
5413 if (groups_flag) {
5414 CONF_ERROR(cmd, "groups already set");
5415 }
5416
5417 if (strcasecmp("groups*", arg) == 0) {
5418 pr_log_debug(DEBUG1,
5419 "%s: use of '*' in SQLAuthenticate has been deprecated. "
5420 "Use AuthOrder for setting authoritativeness",
5421 (char *) cmd->argv[0]);
5422
5423 } else if (strlen(arg) > 6) {
5424 CONF_ERROR(cmd, "unknown argument");
5425 }
5426
5427 authmask |= SQL_AUTH_GROUPS;
5428 groups_flag = TRUE;
5429
5430 } else if (strncasecmp("users", arg, 5) == 0) {
5431 if (users_flag) {
5432 CONF_ERROR(cmd, "users already set");
5433 }
5434
5435 if (strcasecmp("users*", arg) == 0) {
5436 pr_log_debug(DEBUG1,
5437 "%s: use of '*' in SQLAuthenticate has been deprecated. "
5438 "Use AuthOrder for setting authoritativeness",
5439 (char *) cmd->argv[0]);
5440
5441 } else if (strlen(arg) > 5) {
5442 CONF_ERROR(cmd, "unknown argument");
5443 }
5444
5445 authmask |= SQL_AUTH_USERS;
5446 users_flag = TRUE;
5447
5448 } else {
5449 CONF_ERROR(cmd, "unknown argument");
5450 }
5451 }
5452 }
5453
5454 /* Finally, fixup if we've received groupset with no groups,
5455 * or userset with no users
5456 */
5457 if ((groupset_flag && !groups_flag) ||
5458 (userset_flag && !users_flag)) {
5459 CONF_ERROR(cmd, "groupset and userset have no meaning without "
5460 "a corresponding groups or users argument.");
5461 }
5462
5463 c = add_config_param(cmd->argv[0], 1, NULL);
5464 c->argv[0] = pcalloc(c->pool, sizeof(int));
5465 *((int *) c->argv[0]) = authmask;
5466
5467 return PR_HANDLED(cmd);
5468 }
5469
5470 /* logging stuff */
5471
5472 static char *sql_logfile = NULL;
5473 static int sql_logfd = -1;
5474
sql_closelog(void)5475 static int sql_closelog(void) {
5476
5477 /* sanity check */
5478 if (sql_logfd >= 0) {
5479 (void) close(sql_logfd);
5480 }
5481
5482 sql_logfd = -1;
5483 sql_logfile = NULL;
5484
5485 return 0;
5486 }
5487
sql_log(int level,const char * fmt,...)5488 int sql_log(int level, const char *fmt, ...) {
5489 va_list msg;
5490 int res;
5491
5492 /* sanity check */
5493 if (!sql_logfile)
5494 return 0;
5495
5496 va_start(msg, fmt);
5497 res = pr_log_vwritefile(sql_logfd, MOD_SQL_VERSION, fmt, msg);
5498 va_end(msg);
5499
5500 return res;
5501 }
5502
sql_openlog(void)5503 static int sql_openlog(void) {
5504 int res = 0, xerrno = 0;
5505
5506 /* Sanity checks */
5507 sql_logfile = get_param_ptr(main_server->conf, "SQLLogFile", FALSE);
5508 if (sql_logfile == NULL)
5509 return 0;
5510
5511 if (strcasecmp(sql_logfile, "none") == 0) {
5512 sql_logfile = NULL;
5513 return 0;
5514 }
5515
5516 pr_signals_block();
5517 PRIVS_ROOT
5518 res = pr_log_openfile(sql_logfile, &sql_logfd, PR_LOG_SYSTEM_MODE);
5519 xerrno = errno;
5520 PRIVS_RELINQUISH
5521 pr_signals_unblock();
5522
5523 errno = xerrno;
5524 return res;
5525 }
5526
5527 /* usage: SQLConnectInfo info [user [pass [policy]]]
5528 * [ssl-cert:<path>] [ssl-key:<path>] [ssl-ca:/path] [ssl-ciphers:str]
5529 */
set_sqlconnectinfo(cmd_rec * cmd)5530 MODRET set_sqlconnectinfo(cmd_rec *cmd) {
5531 register unsigned int i;
5532 int argc = 0;
5533 char **argv = NULL, *info = NULL, *user = "", *pass = "", *ttl = NULL;
5534 char *ssl_cert_file = NULL, *ssl_key_file = NULL, *ssl_ca_file = NULL;
5535 char *ssl_ca_dir = NULL, *ssl_ciphers = NULL;
5536 array_header *params;
5537
5538 CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL);
5539
5540 if (cmd->argc < 2 ||
5541 cmd->argc > 9) {
5542 CONF_ERROR(cmd, "requires 1 to 8 parameters; check the mod_sql docs");
5543 }
5544
5545 /* First, deal with any required parameters. */
5546 info = cmd->argv[1];
5547
5548 /* Next, search for/process any optional named parameters. */
5549 params = make_array(cmd->tmp_pool, 0, sizeof(char *));
5550
5551 for (i = 2; i < cmd->argc; i++) {
5552 if (strncmp(cmd->argv[i], "ssl-cert:", 9) == 0) {
5553 char *path;
5554
5555 path = cmd->argv[i];
5556
5557 /* Advance past the "ssl-cert:" prefix. */
5558 path += 9;
5559
5560 /* Check the file exists! */
5561 if (file_exists2(cmd->tmp_pool, path) == TRUE) {
5562 ssl_cert_file = path;
5563
5564 } else {
5565 pr_log_pri(PR_LOG_NOTICE, MOD_SQL_VERSION
5566 ": %s: SSL certificate '%s': %s", (char *) cmd->argv[0], path,
5567 strerror(ENOENT));
5568 }
5569
5570 } else if (strncmp(cmd->argv[i], "ssl-key:", 8) == 0) {
5571 char *path;
5572
5573 path = cmd->argv[i];
5574
5575 /* Advance past the "ssl-key:" prefix. */
5576 path += 8;
5577
5578 /* Check the file exists! */
5579 if (file_exists2(cmd->tmp_pool, path) == TRUE) {
5580 ssl_key_file = path;
5581
5582 } else {
5583 pr_log_pri(PR_LOG_NOTICE, MOD_SQL_VERSION
5584 ": %s: SSL certificate key '%s': %s", (char *) cmd->argv[0], path,
5585 strerror(ENOENT));
5586 }
5587
5588 } else if (strncmp(cmd->argv[i], "ssl-ca:", 7) == 0) {
5589 char *path;
5590
5591 path = cmd->argv[i];
5592
5593 /* Advance past the "ssl-ca:" prefix. */
5594 path += 7;
5595
5596 /* Check the file exists! */
5597 if (file_exists2(cmd->tmp_pool, path) == TRUE) {
5598 ssl_ca_file = path;
5599
5600 } else if (dir_exists2(cmd->tmp_pool, path) == TRUE) {
5601 ssl_ca_dir = path;
5602
5603 } else {
5604 pr_log_pri(PR_LOG_NOTICE, MOD_SQL_VERSION
5605 ": %s: SSL CA '%s': %s", (char *) cmd->argv[0], path,
5606 strerror(ENOENT));
5607 }
5608
5609 } else if (strncmp(cmd->argv[i], "ssl-ciphers:", 12) == 0) {
5610 char *ciphers;
5611
5612 ciphers = cmd->argv[i];
5613
5614 /* Advance past the "ssl-ciphers:" prefix. */
5615 ciphers += 12;
5616
5617 ssl_ciphers = ciphers;
5618
5619 } else {
5620 *((char **) push_array(params)) = cmd->argv[i];
5621 }
5622 }
5623
5624 /* Last, handle any optional positional parameters. */
5625
5626 argc = params->nelts;
5627 argv = params->elts;
5628
5629 if (argc >= 1) {
5630 user = argv[0];
5631 }
5632
5633 if (argc >= 2) {
5634 pass = argv[1];
5635 }
5636
5637 if (argc >= 3) {
5638 ttl = argv[2];
5639
5640 } else {
5641 ttl = "0";
5642 }
5643
5644 (void) add_config_param_str(cmd->argv[0], 9, info, user, pass, ttl,
5645 ssl_cert_file, ssl_key_file, ssl_ca_file, ssl_ca_dir, ssl_ciphers);
5646 return PR_HANDLED(cmd);
5647 }
5648
5649 /* usage: SQLEngine on|off|auth|log */
set_sqlengine(cmd_rec * cmd)5650 MODRET set_sqlengine(cmd_rec *cmd) {
5651 config_rec *c;
5652 int engine = -1;
5653
5654 CHECK_ARGS(cmd, 1);
5655 CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL|CONF_ANON);
5656
5657 engine = get_boolean(cmd, 1);
5658 if (engine == -1) {
5659 /* The parameter is not a boolean; check for "auth" or "log". */
5660 if (strcasecmp(cmd->argv[1], "auth") == 0) {
5661 engine = SQL_ENGINE_FL_AUTH;
5662
5663 } else if (strcasecmp(cmd->argv[1], "log") == 0) {
5664 engine = SQL_ENGINE_FL_LOG;
5665
5666 } else {
5667 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unknown SQLEngine parameter '",
5668 cmd->argv[1], "'", NULL));
5669 }
5670
5671 } else {
5672 if (engine == 1) {
5673 /* Convert an "on" into a auth|log combination. */
5674 engine = SQL_ENGINE_FL_AUTH|SQL_ENGINE_FL_LOG;
5675 }
5676 }
5677
5678 c = add_config_param(cmd->argv[0], 1, NULL);
5679 c->argv[0] = pcalloc(c->pool, sizeof(int));
5680 *((int *) c->argv[0]) = engine;
5681 c->flags |= CF_MERGEDOWN;
5682
5683 return PR_HANDLED(cmd);
5684 }
5685
set_sqlauthtypes(cmd_rec * cmd)5686 MODRET set_sqlauthtypes(cmd_rec *cmd) {
5687 array_header *auth_list;
5688 register unsigned int i;
5689
5690 CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL);
5691
5692 /* Need *at least* one handler. */
5693 if (cmd->argc < 2) {
5694 CONF_ERROR(cmd, "expected at least one SQLAuthType");
5695 }
5696
5697 auth_list = make_array(permanent_pool, cmd->argc-1,
5698 sizeof(struct sql_authtype_handler *));
5699
5700 /* Walk through our cmd->argv. */
5701 for (i = 1; i < cmd->argc; i++) {
5702 struct sql_authtype_handler *sah;
5703
5704 sah = sql_get_authtype(cmd->argv[i]);
5705 if (sah == NULL) {
5706 sql_log(DEBUG_WARN, "unknown SQLAuthType '%s'", (char *) cmd->argv[i]);
5707 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unknown SQLAuthType '",
5708 cmd->argv[i], "'", NULL));
5709 }
5710
5711 if (strcasecmp(sah->name, "Plaintext") == 0) {
5712 pr_log_pri(PR_LOG_WARNING, MOD_SQL_VERSION
5713 ": WARNING: Use of Plaintext SQLAuthType is insecure, as it allows "
5714 "storage of passwords IN THE CLEAR in your database tables!");
5715 }
5716
5717 *((struct sql_authtype_handler **) push_array(auth_list)) = sah;
5718 }
5719
5720 (void) add_config_param(cmd->argv[0], 1, auth_list);
5721 return PR_HANDLED(cmd);
5722 }
5723
5724 /* usage: SQLBackend name */
set_sqlbackend(cmd_rec * cmd)5725 MODRET set_sqlbackend(cmd_rec *cmd) {
5726 CHECK_ARGS(cmd, 1);
5727 CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL);
5728
5729 add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
5730 return PR_HANDLED(cmd);
5731 }
5732
set_sqlminid(cmd_rec * cmd)5733 MODRET set_sqlminid(cmd_rec *cmd) {
5734 config_rec *c;
5735 uid_t uid;
5736 gid_t gid;
5737
5738 CHECK_ARGS(cmd, 1);
5739 CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL);
5740
5741 if (pr_str2uid(cmd->argv[1], &uid) < 0) {
5742 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "invalid UID value '",
5743 cmd->argv[1], "'", NULL));
5744 }
5745
5746 if (pr_str2gid(cmd->argv[1], &gid) < 0) {
5747 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "invalid GID value '",
5748 cmd->argv[1], "'", NULL));
5749 }
5750
5751 c = add_config_param(cmd->argv[0], 2, NULL, NULL);
5752 c->argv[0] = pcalloc(c->pool, sizeof(uid_t));
5753 *((uid_t *) c->argv[0]) = uid;
5754 c->argv[1] = pcalloc(c->pool, sizeof(gid_t));
5755 *((gid_t *) c->argv[1]) = gid;
5756
5757 return PR_HANDLED(cmd);
5758 }
5759
set_sqlminuseruid(cmd_rec * cmd)5760 MODRET set_sqlminuseruid(cmd_rec *cmd) {
5761 config_rec *c = NULL;
5762 uid_t uid;
5763
5764 CHECK_ARGS(cmd, 1);
5765 CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL);
5766
5767 if (pr_str2uid(cmd->argv[1], &uid) < 0) {
5768 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "invalid UID value '",
5769 cmd->argv[1], "'", NULL));
5770 }
5771
5772 c = add_config_param(cmd->argv[0], 1, NULL);
5773 c->argv[0] = pcalloc(c->pool, sizeof(uid_t));
5774 *((uid_t *) c->argv[0]) = uid;
5775
5776 return PR_HANDLED(cmd);
5777 }
5778
set_sqlminusergid(cmd_rec * cmd)5779 MODRET set_sqlminusergid(cmd_rec *cmd) {
5780 config_rec *c = NULL;
5781 gid_t gid;
5782
5783 CHECK_ARGS(cmd, 1);
5784 CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL);
5785
5786 if (pr_str2gid(cmd->argv[1], &gid) < 0) {
5787 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "invalid GID value '",
5788 cmd->argv[1], "'", NULL));
5789 }
5790
5791 c = add_config_param(cmd->argv[0], 1, NULL);
5792 c->argv[0] = pcalloc(c->pool, sizeof(gid_t));
5793 *((gid_t *) c->argv[0]) = gid;
5794
5795 return PR_HANDLED(cmd);
5796 }
5797
set_sqldefaultuid(cmd_rec * cmd)5798 MODRET set_sqldefaultuid(cmd_rec *cmd) {
5799 config_rec *c;
5800 uid_t uid;
5801
5802 CHECK_ARGS(cmd, 1);
5803 CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL);
5804
5805 if (pr_str2uid(cmd->argv[1], &uid) < 0) {
5806 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "invalid UID value '",
5807 cmd->argv[1], "'", NULL));
5808 }
5809
5810 c = add_config_param(cmd->argv[0], 1, NULL);
5811 c->argv[0] = pcalloc(c->pool, sizeof(uid_t));
5812 *((uid_t *) c->argv[0]) = uid;
5813
5814 return PR_HANDLED(cmd);
5815 }
5816
set_sqldefaultgid(cmd_rec * cmd)5817 MODRET set_sqldefaultgid(cmd_rec *cmd) {
5818 config_rec *c;
5819 gid_t gid;
5820
5821 CHECK_ARGS(cmd, 1);
5822 CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL);
5823
5824 if (pr_str2gid(cmd->argv[1], &gid) < 0) {
5825 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "invalid GID value '",
5826 cmd->argv[1], "'", NULL));
5827 }
5828
5829 c = add_config_param(cmd->argv[0], 1, NULL);
5830 c->argv[0] = pcalloc(c->pool, sizeof(gid_t));
5831 *((gid_t *) c->argv[0]) = gid;
5832
5833 return PR_HANDLED(cmd);
5834 }
5835
5836 /* Event handlers
5837 */
5838
sql_chroot_ev(const void * event_data,void * user_data)5839 static void sql_chroot_ev(const void *event_data, void *user_data) {
5840 /* Loop through our list of named connections, making sure that any
5841 * with a connection policy of PERSESSION are opened.
5842 */
5843 if (sql_named_conns != NULL) {
5844 pool *tmp_pool;
5845 struct sql_named_conn *snc;
5846
5847 tmp_pool = make_sub_pool(session.pool);
5848
5849 for (snc = sql_named_conns; snc; snc = snc->next) {
5850 pr_signals_handle();
5851
5852 if (snc->conn_policy == SQL_CONN_POLICY_PERSESSION) {
5853 cmd_rec *cmd;
5854 modret_t *mr;
5855
5856 cmd = sql_make_cmd(tmp_pool, 1, snc->conn_name);
5857 mr = sql_dispatch(cmd, "sql_open");
5858 (void) check_response(mr, 0);
5859 SQL_FREE_CMD(cmd);
5860 }
5861 }
5862
5863 destroy_pool(tmp_pool);
5864 }
5865 }
5866
sql_exit_ev(const void * event_data,void * user_data)5867 static void sql_exit_ev(const void *event_data, void *user_data) {
5868 config_rec *c;
5869 cmd_rec *cmd;
5870 modret_t *mr;
5871
5872 if (cmap.engine == 0) {
5873 return;
5874 }
5875
5876 /* handle EXIT queries */
5877 c = find_config(main_server->conf, CONF_PARAM, "SQLLog_EXIT", FALSE);
5878 while (c != NULL) {
5879 pr_signals_handle();
5880
5881 /* Since we're exiting the process here (or soon, anyway), we can
5882 * get away with using the config_rec's pool.
5883 */
5884 cmd = sql_make_cmd(c->pool, 1, "EXIT");
5885
5886 /* Ignore errors; we're exiting anyway. */
5887 (void) process_sqllog(cmd, c, "exit_listener", SQL_LOG_FL_IGNORE_ERRORS);
5888
5889 c = find_config_next(c, c->next, CONF_PARAM, "SQLLog_EXIT", FALSE);
5890 }
5891
5892 cmd = sql_make_cmd(session.pool, 0);
5893 mr = sql_dispatch(cmd, "sql_exit");
5894 (void) check_response(mr, SQL_LOG_FL_IGNORE_ERRORS);
5895
5896 sql_closelog();
5897 return;
5898 }
5899
5900 #if defined(PR_SHARED_MODULE)
sql_mod_unload_ev(const void * event_data,void * user_data)5901 static void sql_mod_unload_ev(const void *event_data, void *user_data) {
5902 if (strcmp("mod_sql.c", (const char *) event_data) != 0) {
5903 return;
5904 }
5905
5906 destroy_pool(sql_pool);
5907 sql_pool = NULL;
5908 sql_backends = NULL;
5909 sql_auth_list = NULL;
5910
5911 pr_event_unregister(&sql_module, NULL, NULL);
5912
5913 pr_timer_remove(-1, &sql_module);
5914 sql_keepalive_timer_id = -1;
5915 sql_keepalive_stmt = NULL;
5916
5917 (void) sql_unregister_authtype("Crypt");
5918 (void) sql_unregister_authtype("Empty");
5919 (void) sql_unregister_authtype("Plaintext");
5920
5921 #if defined(HAVE_OPENSSL) || defined(PR_USE_OPENSSL)
5922 (void) sql_unregister_authtype("OpenSSL");
5923 #endif /* HAVE_OPENSSL */
5924
5925 close(sql_logfd);
5926 sql_logfd = -1;
5927 sql_logfile = NULL;
5928 }
5929
5930 #else
5931
sql_preparse_ev(const void * event_data,void * user_data)5932 static void sql_preparse_ev(const void *event_data, void *user_data) {
5933 /* If no backends have been registered, croak. */
5934 if (sql_nbackends == 0) {
5935 pr_log_pri(PR_LOG_NOTICE, MOD_SQL_VERSION
5936 ": notice: no backend modules have been registered");
5937 exit(1);
5938 }
5939 }
5940 #endif /* PR_SHARED_MODULE */
5941
sql_eventlog_ev(const void * event_data,void * user_data)5942 static void sql_eventlog_ev(const void *event_data, void *user_data) {
5943 const char *event_name;
5944 int res;
5945
5946 event_name = user_data;
5947 res = eventlog_master(event_name);
5948 if (res < 0) {
5949 sql_log(DEBUG_FUNC, "SQLLogOnEvent '%s' query failed", event_name);
5950 }
5951 }
5952
sql_sess_reinit_ev(const void * event_data,void * user_data)5953 static void sql_sess_reinit_ev(const void *event_data, void *user_data) {
5954 config_rec *c;
5955 int res;
5956
5957 /* A HOST command changed the main_server pointer; reinitialize ourselves. */
5958
5959 pr_event_unregister(&sql_module, "core.chroot", sql_chroot_ev);
5960 pr_event_unregister(&sql_module, "core.exit", sql_exit_ev);
5961 pr_event_unregister(&sql_module, "core.session-reinit", sql_sess_reinit_ev);
5962
5963 pr_timer_remove(-1, &sql_module);
5964 sql_keepalive_timer_id = -1;
5965 sql_keepalive_stmt = NULL;
5966
5967 c = find_config(session.prev_server->conf, CONF_PARAM, "SQLLogOnEvent",
5968 FALSE);
5969 while (c != NULL) {
5970 char *event_name;
5971
5972 pr_signals_handle();
5973
5974 event_name = c->argv[0];
5975
5976 pr_event_unregister(&sql_module, event_name, sql_eventlog_ev);
5977 c = find_config_next(c, c->next, CONF_PARAM, "SQLLogOnEvent", FALSE);
5978 }
5979
5980 pr_sql_opts = 0UL;
5981 pr_sql_conn_policy = 0;
5982
5983 if (sql_logfd >= 0) {
5984 (void) close(sql_logfd);
5985 sql_logfd = -1;
5986 sql_logfile = NULL;
5987 }
5988
5989 memset(&cmap, 0, sizeof(cmap));
5990 sql_cmdtable = NULL;
5991 sql_default_cmdtable = NULL;
5992
5993 res = sql_sess_init();
5994 if (res < 0) {
5995 pr_session_disconnect(&sql_module, PR_SESS_DISCONNECT_SESSION_INIT_FAILED,
5996 NULL);
5997 }
5998 }
5999
6000 /* Initialization routines
6001 */
6002
sql_init(void)6003 static int sql_init(void) {
6004 #if defined(PR_SHARED_MODULE)
6005 pr_event_register(&sql_module, "core.module-unload", sql_mod_unload_ev, NULL);
6006 #else
6007 pr_event_register(&sql_module, "core.preparse", sql_preparse_ev, NULL);
6008 #endif /* PR_SHARED_MODULE */
6009
6010 /* Register our built-in auth handlers. */
6011 (void) sql_register_authtype("Crypt", sql_auth_crypt);
6012 (void) sql_register_authtype("Empty", sql_auth_empty);
6013 (void) sql_register_authtype("Plaintext", sql_auth_plaintext);
6014
6015 #if defined(HAVE_OPENSSL) || defined(PR_USE_OPENSSL)
6016 (void) sql_register_authtype("OpenSSL", sql_auth_openssl);
6017 #endif /* HAVE_OPENSSL */
6018
6019 return 0;
6020 }
6021
sql_sess_init(void)6022 static int sql_sess_init(void) {
6023 char *authstr = NULL;
6024 config_rec *c = NULL;
6025 void *default_backend = NULL, *ptr = NULL;
6026 unsigned char *negative_cache = NULL;
6027 cmd_rec *cmd = NULL;
6028 modret_t *mr = NULL;
6029 sql_data_t *sd = NULL;
6030 int engine = 0, res = 0;
6031 char *fieldset = NULL;
6032 pool *tmp_pool = NULL;
6033
6034 pr_event_register(&sql_module, "core.session-reinit", sql_sess_reinit_ev,
6035 NULL);
6036
6037 /* Build a temporary pool */
6038 tmp_pool = make_sub_pool(session.pool);
6039
6040 /* Open any configured SQLLogFile */
6041 res = sql_openlog();
6042 if (res < 0) {
6043 if (res == -1) {
6044 pr_log_pri(PR_LOG_NOTICE, "notice: unable to open SQLLogFile: %s",
6045 strerror(errno));
6046
6047 } else if (res == PR_LOG_WRITABLE_DIR) {
6048 pr_log_pri(PR_LOG_WARNING, "notice: unable to open SQLLogFile: "
6049 "parent directory is world-writable");
6050
6051 } else if (res == PR_LOG_SYMLINK) {
6052 pr_log_pri(PR_LOG_WARNING, "notice: unable to open SQLLogFile: "
6053 "cannot log to a symbolic link");
6054 }
6055 }
6056
6057 default_backend = get_param_ptr(main_server->conf, "SQLBackend", FALSE);
6058 sql_default_cmdtable = sql_set_backend(default_backend);
6059 if (sql_default_cmdtable == NULL) {
6060 if (default_backend != NULL) {
6061 sql_log(DEBUG_INFO, "unable to load '%s' SQL backend: %s",
6062 (char *) default_backend, strerror(errno));
6063
6064 } else {
6065 sql_log(DEBUG_INFO, "unable to load SQL backend: %s", strerror(errno));
6066 }
6067
6068 destroy_pool(tmp_pool);
6069 return -1;
6070 }
6071
6072 if (default_backend != NULL) {
6073 pr_trace_msg(trace_channel, 9, "loaded '%s' SQL backend",
6074 (char *) default_backend);
6075 }
6076
6077 /* Construct our internal cache structure for this session. */
6078 memset(&cmap, 0, sizeof(cmap));
6079
6080 c = find_config(main_server->conf, CONF_PARAM, "SQLEngine", FALSE);
6081 if (c != NULL) {
6082 cmap.engine = engine = *((int *) c->argv[0]);
6083
6084 } else {
6085 cmap.engine = engine = (SQL_ENGINE_FL_AUTH|SQL_ENGINE_FL_LOG);
6086 }
6087
6088 if (cmap.engine == 0) {
6089 destroy_pool(tmp_pool);
6090 return 0;
6091 }
6092
6093 /* Get our backend info and toss it up */
6094 cmd = sql_make_cmd(tmp_pool, 1, "foo");
6095 mr = sql_dispatch(cmd, "sql_identify");
6096 if (check_response(mr, 0) < 0) {
6097 destroy_pool(tmp_pool);
6098 return -1;
6099 }
6100
6101 sd = (sql_data_t *) mr->data;
6102
6103 sql_log(DEBUG_INFO, "backend module '%s'", sd->data[0]);
6104 sql_log(DEBUG_INFO, "backend api '%s'", sd->data[1]);
6105
6106 SQL_FREE_CMD(cmd);
6107
6108 sql_log(DEBUG_FUNC, "%s", ">>> sql_sess_init");
6109
6110 if (sql_pool == NULL) {
6111 sql_pool = make_sub_pool(session.pool);
6112 pr_pool_tag(sql_pool, MOD_SQL_VERSION);
6113 }
6114
6115 group_name_cache = make_cache(sql_pool, _group_name, _groupcmp);
6116 passwd_name_cache = make_cache(sql_pool, _passwd_name, _passwdcmp);
6117 group_gid_cache = make_cache(sql_pool, _group_gid, _groupcmp);
6118 passwd_uid_cache = make_cache(sql_pool, _passwd_uid, _passwdcmp);
6119
6120 cmap.group_cache_filled = 0;
6121 cmap.passwd_cache_filled = 0;
6122
6123 cmap.curr_group = NULL;
6124 cmap.curr_passwd = NULL;
6125
6126 ptr = get_param_ptr(main_server->conf, "SQLAuthenticate", FALSE);
6127 if (ptr != NULL) {
6128 cmap.authmask = *((int *) ptr);
6129
6130 } else {
6131 cmap.authmask = SQL_AUTH_GROUPS|SQL_AUTH_USERS|SQL_AUTH_GROUPSET|
6132 SQL_AUTH_USERSET;
6133 }
6134
6135 negative_cache = get_param_ptr(main_server->conf, "SQLNegativeCache",
6136 FALSE);
6137 cmap.negative_cache = negative_cache ? *negative_cache : FALSE;
6138
6139 cmap.defaulthomedir = get_param_ptr(main_server->conf, "SQLDefaultHomedir",
6140 FALSE);
6141
6142 pr_sql_opts = 0UL;
6143 c = find_config(main_server->conf, CONF_PARAM, "SQLOptions", FALSE);
6144 while (c != NULL) {
6145 unsigned long opts;
6146
6147 pr_signals_handle();
6148
6149 opts = *((unsigned long *) c->argv[0]);
6150 pr_sql_opts |= opts;
6151
6152 c = find_config_next(c, c->next, CONF_PARAM, "SQLOptions", FALSE);
6153 }
6154
6155 ptr = get_param_ptr(main_server->conf, "SQLUserTable", FALSE);
6156
6157 /* if we have no SQLUserTable, SQLUserInfo was not used -- default all */
6158
6159 if (ptr == NULL) {
6160 cmap.usrtable = MOD_SQL_DEF_USERTABLE;
6161 cmap.usrfield = MOD_SQL_DEF_USERNAMEFIELD;
6162 cmap.pwdfield = MOD_SQL_DEF_USERPASSWORDFIELD;
6163 cmap.uidfield = MOD_SQL_DEF_USERUIDFIELD;
6164 cmap.gidfield = MOD_SQL_DEF_USERGIDFIELD;
6165 cmap.homedirfield = MOD_SQL_DEF_USERHOMEDIRFIELD;
6166 cmap.shellfield = MOD_SQL_DEF_USERSHELLFIELD;
6167
6168 /* It's possible that custom UserInfo queries were configured. Check for
6169 * them.
6170 */
6171 ptr = get_param_ptr(main_server->conf, "SQLCustomUserInfoByName", FALSE);
6172 if (ptr != NULL) {
6173 config_rec *custom_c = NULL;
6174 char *named_query = pstrcat(tmp_pool, "SQLNamedQuery_", ptr, NULL);
6175
6176 custom_c = find_config(main_server->conf, CONF_PARAM, named_query, FALSE);
6177 if (custom_c == NULL) {
6178 sql_log(DEBUG_INFO, "error: unable to resolve custom "
6179 "SQLNamedQuery name '%s'", (char *) ptr);
6180
6181 } else {
6182 cmap.usercustom = ptr;
6183 }
6184 }
6185
6186 ptr = get_param_ptr(main_server->conf, "SQLCustomUserInfoByID", FALSE);
6187 if (ptr != NULL) {
6188 config_rec *custom_c = NULL;
6189 char *named_query = pstrcat(tmp_pool, "SQLNamedQuery_", ptr, NULL);
6190
6191 custom_c = find_config(main_server->conf, CONF_PARAM, named_query, FALSE);
6192 if (custom_c == NULL) {
6193 sql_log(DEBUG_INFO, "error: unable to resolve custom "
6194 "SQLNamedQuery name '%s'", (char *) ptr);
6195
6196 } else {
6197 cmap.usercustombyid = ptr;
6198 }
6199 }
6200
6201 ptr = get_param_ptr(main_server->conf, "SQLCustomUserInfoAllNames", FALSE);
6202 if (ptr) {
6203 config_rec *custom_c = NULL;
6204 char *named_query = pstrcat(tmp_pool, "SQLNamedQuery_", ptr, NULL);
6205
6206 custom_c = find_config(main_server->conf, CONF_PARAM, named_query, FALSE);
6207 if (custom_c == NULL) {
6208 sql_log(DEBUG_INFO, "error: unable to resolve custom "
6209 "SQLNamedQuery name '%s'", (char *) ptr);
6210
6211 } else {
6212 cmap.usercustomuserset = ptr;
6213 }
6214 }
6215
6216 ptr = get_param_ptr(main_server->conf, "SQLCustomUserInfoAllUsers", FALSE);
6217 if (ptr != NULL) {
6218 config_rec *custom_c = NULL;
6219 char *named_query = pstrcat(tmp_pool, "SQLNamedQuery_", ptr, NULL);
6220
6221 custom_c = find_config(main_server->conf, CONF_PARAM, named_query, FALSE);
6222 if (custom_c == NULL) {
6223 sql_log(DEBUG_INFO, "error: unable to resolve custom "
6224 "SQLNamedQuery name '%s'", (char *) ptr);
6225
6226 } else {
6227 cmap.usercustomusersetfast = ptr;
6228 }
6229 }
6230
6231 if (cmap.usercustomuserset &&
6232 cmap.usercustomusersetfast &&
6233 strcmp(cmap.usercustomuserset, cmap.usercustomusersetfast) == 0) {
6234 sql_log(DEBUG_INFO, "warning: 'userset' and 'usersetfast' custom "
6235 "SQLUserInfo SQLNamedQuery are the same query ('%s'), probable "
6236 "misconfiguration", cmap.usercustomuserset);
6237 }
6238
6239 } else {
6240 cmap.usrtable = ptr;
6241 cmap.usrfield = get_param_ptr(main_server->conf, "SQLUsernameField", FALSE);
6242 cmap.pwdfield = get_param_ptr(main_server->conf, "SQLPasswordField", FALSE);
6243 cmap.uidfield = get_param_ptr(main_server->conf, "SQLUidField", FALSE);
6244 cmap.gidfield = get_param_ptr(main_server->conf, "SQLGidField", FALSE);
6245 cmap.homedirfield = get_param_ptr(main_server->conf, "SQLHomedirField",
6246 FALSE);
6247 cmap.shellfield = get_param_ptr(main_server->conf, "SQLShellField", FALSE);
6248 }
6249
6250 /* Build the userfieldset */
6251 fieldset = pstrcat(tmp_pool, cmap.usrfield, ", ", cmap.pwdfield, NULL);
6252
6253 if (cmap.uidfield)
6254 fieldset = pstrcat(tmp_pool, fieldset, ", ", cmap.uidfield, NULL);
6255
6256 if (cmap.gidfield)
6257 fieldset = pstrcat(tmp_pool, fieldset, ", ", cmap.gidfield, NULL);
6258
6259 if (cmap.homedirfield)
6260 fieldset = pstrcat(tmp_pool, fieldset, ", ", cmap.homedirfield, NULL);
6261
6262 if (cmap.shellfield)
6263 fieldset = pstrcat(tmp_pool, fieldset, ", ", cmap.shellfield, NULL);
6264
6265 cmap.usrfields = pstrdup(sql_pool, fieldset);
6266
6267 ptr = get_param_ptr(main_server->conf, "SQLGroupTable", FALSE);
6268
6269 /* If we have no ptr, SQLGroupInfo was not used - default all */
6270 if (ptr == NULL) {
6271 cmap.grptable = MOD_SQL_DEF_GROUPTABLE;
6272 cmap.grpfield = MOD_SQL_DEF_GROUPNAMEFIELD;
6273 cmap.grpgidfield = MOD_SQL_DEF_GROUPGIDFIELD;
6274 cmap.grpmembersfield = MOD_SQL_DEF_GROUPMEMBERSFIELD;
6275
6276 /* Check for any configured custom SQLGroupInfo queries. */
6277
6278 ptr = get_param_ptr(main_server->conf, "SQLCustomGroupInfoByID", FALSE);
6279 if (ptr != NULL) {
6280 config_rec *custom_c = NULL;
6281 char *named_query = pstrcat(tmp_pool, "SQLNamedQuery_", ptr, NULL);
6282
6283 custom_c = find_config(main_server->conf, CONF_PARAM, named_query, FALSE);
6284 if (custom_c == NULL) {
6285 sql_log(DEBUG_INFO, "error: unable to resolve custom "
6286 "SQLNamedQuery name '%s'", (char *) ptr);
6287
6288 } else {
6289 cmap.groupcustombyid = ptr;
6290 cmap.grpgidfield = NULL;
6291 }
6292 }
6293
6294 ptr = get_param_ptr(main_server->conf, "SQLCustomGroupInfoByName", FALSE);
6295 if (ptr != NULL) {
6296 config_rec *custom_c = NULL;
6297 char *named_query = pstrcat(tmp_pool, "SQLNamedQuery_", ptr, NULL);
6298
6299 custom_c = find_config(main_server->conf, CONF_PARAM, named_query, FALSE);
6300 if (custom_c == NULL) {
6301 sql_log(DEBUG_INFO, "error: unable to resolve custom "
6302 "SQLNamedQuery name '%s'", (char *) ptr);
6303
6304 } else {
6305 cmap.groupcustombyname = ptr;
6306 }
6307 }
6308
6309 ptr = get_param_ptr(main_server->conf, "SQLCustomGroupInfoMembers", FALSE);
6310 if (ptr != NULL) {
6311 config_rec *custom_c = NULL;
6312 char *named_query = pstrcat(tmp_pool, "SQLNamedQuery_", ptr, NULL);
6313
6314 custom_c = find_config(main_server->conf, CONF_PARAM, named_query, FALSE);
6315 if (custom_c == NULL) {
6316 sql_log(DEBUG_INFO, "error: unable to resolve custom "
6317 "SQLNamedQuery name '%s'", (char *) ptr);
6318
6319 } else {
6320 cmap.groupcustommembers = ptr;
6321 }
6322 }
6323
6324 ptr = get_param_ptr(main_server->conf, "SQLCustomGroupInfoAllNames", FALSE);
6325 if (ptr != NULL) {
6326 config_rec *custom_c = NULL;
6327 char *named_query = pstrcat(tmp_pool, "SQLNamedQuery_", ptr, NULL);
6328
6329 custom_c = find_config(main_server->conf, CONF_PARAM, named_query, FALSE);
6330 if (custom_c == NULL) {
6331 sql_log(DEBUG_INFO, "error: unable to resolve custom "
6332 "SQLNamedQuery name '%s'", (char *) ptr);
6333
6334 } else {
6335 cmap.groupcustomgroupset = ptr;
6336 }
6337 }
6338
6339 ptr = get_param_ptr(main_server->conf, "SQLCustomGroupInfoAllGroups",
6340 FALSE);
6341 if (ptr != NULL) {
6342 config_rec *custom_c = NULL;
6343 char *named_query = pstrcat(tmp_pool, "SQLNamedQuery_", ptr, NULL);
6344
6345 custom_c = find_config(main_server->conf, CONF_PARAM, named_query, FALSE);
6346 if (custom_c == NULL) {
6347 sql_log(DEBUG_INFO, "error: unable to resolve custom "
6348 "SQLNamedQuery name '%s'", (char *) ptr);
6349
6350 } else {
6351 cmap.groupcustomgroupsetfast = ptr;
6352 }
6353 }
6354
6355 } else {
6356 cmap.grptable = get_param_ptr(main_server->conf, "SQLGroupTable", FALSE);
6357 cmap.grpfield = get_param_ptr(main_server->conf, "SQLGroupnameField",
6358 FALSE);
6359 cmap.grpgidfield = get_param_ptr(main_server->conf, "SQLGroupGIDField",
6360 FALSE);
6361 cmap.grpmembersfield = get_param_ptr(main_server->conf,
6362 "SQLGroupMembersField", FALSE);
6363 }
6364
6365 /* Build the groupfieldset */
6366 fieldset = pstrcat(tmp_pool, cmap.grpfield, ", ", cmap.grpgidfield, ", ",
6367 cmap.grpmembersfield, NULL);
6368 cmap.grpfields = pstrdup(sql_pool, fieldset);
6369
6370 ptr = get_param_ptr(main_server->conf, "SQLUserWhereClause", FALSE);
6371 cmap.userwhere = ptr ? pstrcat(sql_pool, "(", ptr, ")", NULL) : NULL;
6372
6373 ptr = get_param_ptr(main_server->conf, "SQLGroupWhereClause", FALSE);
6374 cmap.groupwhere = ptr ? pstrcat(sql_pool, "(", ptr, ")", NULL) : NULL;
6375
6376 ptr = get_param_ptr(main_server->conf, "SQLAuthTypes", FALSE);
6377 cmap.auth_list = ptr;
6378
6379 if (cmap.auth_list == NULL &&
6380 cmap.authmask != 0 &&
6381 (engine > 0 && engine != SQL_ENGINE_FL_LOG)) {
6382 sql_log(DEBUG_INFO, "%s", "error: no SQLAuthTypes configured");
6383 }
6384
6385 c = find_config(main_server->conf, CONF_PARAM, "SQLMinID", FALSE);
6386 if (c != NULL) {
6387 cmap.minuseruid = *((uid_t *) c->argv[0]);
6388 cmap.minusergid = *((gid_t *) c->argv[1]);
6389
6390 } else {
6391 ptr = get_param_ptr(main_server->conf, "SQLMinUserUID", FALSE);
6392 cmap.minuseruid = ptr ? *((uid_t *) ptr) : MOD_SQL_MIN_USER_UID;
6393
6394 ptr = get_param_ptr(main_server->conf, "SQLMinUserGID", FALSE);
6395 cmap.minusergid = ptr ? *((gid_t *) ptr) : MOD_SQL_MIN_USER_GID;
6396 }
6397
6398 ptr = get_param_ptr(main_server->conf, "SQLDefaultUID", FALSE);
6399 cmap.defaultuid = ptr ? *((uid_t *) ptr) : MOD_SQL_DEF_UID;
6400
6401 ptr = get_param_ptr(main_server->conf, "SQLDefaultGID", FALSE);
6402 cmap.defaultgid = ptr ? *((gid_t *) ptr) : MOD_SQL_DEF_GID;
6403
6404 c = find_config(main_server->conf, CONF_PARAM, "SQLRatioStats", FALSE);
6405 if (c) {
6406 cmap.sql_fstor = c->argv[0];
6407 cmap.sql_fretr = c->argv[1];
6408 cmap.sql_bstor = c->argv[2];
6409 cmap.sql_bretr = c->argv[3];
6410 }
6411
6412 c = find_config(main_server->conf, CONF_PARAM, "SQLRatios", FALSE);
6413 if (c) {
6414 if (!cmap.sql_fstor) {
6415 pr_log_pri(PR_LOG_WARNING,
6416 MOD_SQL_VERSION ": warning: SQLRatios directive ineffective "
6417 "without SQLRatioStats on");
6418 sql_log(DEBUG_WARN, "%s", "warning: SQLRatios directive ineffective "
6419 "without SQLRatioStats on");
6420 }
6421
6422 cmap.sql_frate = c->argv[0];
6423 cmap.sql_fcred = c->argv[1];
6424 cmap.sql_brate = c->argv[2];
6425 cmap.sql_bcred = c->argv[3];
6426 }
6427
6428 if (!cmap.homedirfield &&
6429 !cmap.defaulthomedir) {
6430 cmap.authmask ^= SQL_AUTH_USERS;
6431
6432 pr_log_pri(PR_LOG_WARNING, MOD_SQL_VERSION
6433 ": warning: no homedir field and no default specified. "
6434 "User authentication is OFF");
6435 sql_log(DEBUG_WARN, "%s",
6436 "warning: no homedir field and no default specified. "
6437 "User authentication is OFF");
6438 }
6439
6440 c = find_config(main_server->conf, CONF_PARAM, "SQLConnectInfo", FALSE);
6441 if (c == NULL) {
6442 cmap.authmask = 0;
6443 cmap.engine = 0;
6444 cmap.sql_fstor = NULL;
6445 cmap.sql_frate = NULL;
6446 sql_log(DEBUG_WARN, "%s",
6447 "warning: no SQLConnectInfo specified. mod_sql is OFF");
6448
6449 } else {
6450 unsigned int default_conn_policy;
6451
6452 pr_sql_conn_policy = SQL_CONN_POLICY_PERSESSION;
6453
6454 if (strcasecmp(c->argv[3], "perconn") == 0 ||
6455 strcasecmp(c->argv[3], "perconnection") == 0) {
6456 pr_sql_conn_policy = SQL_CONN_POLICY_PERCONN;
6457
6458 } else if (strcasecmp(c->argv[3], "percall") == 0) {
6459 pr_sql_conn_policy = SQL_CONN_POLICY_PERCALL;
6460 }
6461
6462 if (sql_define_conn(tmp_pool, MOD_SQL_DEF_CONN_NAME, c->argv[1], c->argv[2],
6463 c->argv[0], c->argv[3], c->argv[4], c->argv[5], c->argv[6], c->argv[7],
6464 c->argv[8]) < 0) {
6465 return -1;
6466 }
6467
6468 /* Since sql_define_conn() can change the policy (depending on the
6469 * backend's handling of the connection parameters, we record the
6470 * default connection policy here.
6471 */
6472 default_conn_policy = pr_sql_conn_policy;
6473
6474 sql_log(DEBUG_INFO, "%s", "connection 'default' successfully established");
6475
6476 c = find_config(main_server->conf, CONF_PARAM, "SQLNamedConnectInfo",
6477 FALSE);
6478 while (c != NULL) {
6479 struct sql_named_conn *snc;
6480 const char *conn_name;
6481
6482 pr_signals_handle();
6483
6484 conn_name = c->argv[0];
6485
6486 /* Avoid duplicated connection names. */
6487 if (get_named_conn_backend(conn_name) == NULL) {
6488 pr_sql_conn_policy = SQL_CONN_POLICY_PERSESSION;
6489
6490 if (strncasecmp(c->argv[5], "perconn", 8) == 0 ||
6491 strncasecmp(c->argv[5], "perconnection", 14) == 0) {
6492 pr_sql_conn_policy = SQL_CONN_POLICY_PERCONN;
6493
6494 } else if (strncasecmp(c->argv[5], "percall", 8) == 0) {
6495 pr_sql_conn_policy = SQL_CONN_POLICY_PERCALL;
6496 }
6497
6498 /* Make sure we set the correct backend driver here, so that we
6499 * dispatch to the correct module's command table when defining the
6500 * connection.
6501 */
6502 sql_set_backend(c->argv[1]);
6503
6504 if (sql_define_conn(tmp_pool, c->argv[0], c->argv[3], c->argv[4],
6505 c->argv[2], c->argv[5], c->argv[6], c->argv[7], c->argv[8],
6506 c->argv[9], c->argv[10]) < 0) {
6507 /* Restore the default connection policy. */
6508 pr_sql_conn_policy = default_conn_policy;
6509
6510 destroy_pool(tmp_pool);
6511 return -1;
6512 }
6513
6514 /* Add the mapping of the connection name to the backend to the
6515 * lookup list.
6516 */
6517 snc = pcalloc(sql_pool, sizeof(struct sql_named_conn));
6518 snc->conn_name = conn_name;
6519 snc->conn_policy = pr_sql_conn_policy;
6520 snc->backend = c->argv[1];
6521
6522 if (sql_named_conns != NULL) {
6523 sql_named_conns->prev = snc;
6524 snc->next = sql_named_conns;
6525 }
6526
6527 sql_named_conns = snc;
6528
6529 /* Restore the default connection policy. */
6530 pr_sql_conn_policy = default_conn_policy;
6531
6532 sql_log(DEBUG_INFO, "connection '%s' successfully established",
6533 conn_name );
6534
6535 } else {
6536 sql_log(DEBUG_INFO, MOD_SQL_VERSION
6537 ": unable to open SQLNamedConnectInfo '%s': another connection "
6538 "with that name already exists", conn_name);
6539 }
6540
6541 c = find_config_next(c, c->next, CONF_PARAM, "SQLNamedConnectInfo",
6542 FALSE);
6543 }
6544 }
6545
6546 /* Make sure we use the default SQLBackend here, after processing any
6547 * SQLNamedConnectInfos.
6548 */
6549 if (default_backend != NULL) {
6550 sql_set_backend(default_backend);
6551 }
6552
6553 c = find_config(main_server->conf, CONF_PARAM, "SQLLogOnEvent", FALSE);
6554 while (c != NULL) {
6555 char *event_name;
6556
6557 pr_signals_handle();
6558
6559 event_name = c->argv[0];
6560
6561 pr_event_register(&sql_module, event_name, sql_eventlog_ev, event_name);
6562 c = find_config_next(c, c->next, CONF_PARAM, "SQLLogOnEvent", FALSE);
6563 }
6564
6565 if (cmap.engine == 0) {
6566 sql_log(DEBUG_INFO, "mod_sql engine : off");
6567
6568 } else if (cmap.engine == (SQL_ENGINE_FL_AUTH|SQL_ENGINE_FL_LOG)) {
6569 sql_log(DEBUG_INFO, "mod_sql engine : on");
6570
6571 } else if (cmap.engine == SQL_ENGINE_FL_AUTH) {
6572 sql_log(DEBUG_INFO, "mod_sql engine : auth");
6573
6574 } else if (cmap.engine == SQL_ENGINE_FL_LOG) {
6575 sql_log(DEBUG_INFO, "mod_sql engine : log");
6576 }
6577
6578 sql_log(DEBUG_INFO, "negative_cache : %s", cmap.negative_cache ? "on" : "off");
6579
6580 authstr = "";
6581
6582 if (SQL_USERS)
6583 authstr = pstrcat(tmp_pool, authstr, "users ", NULL);
6584
6585 if (SQL_GROUPS)
6586 authstr = pstrcat(tmp_pool, authstr, "groups ", NULL);
6587
6588 if (SQL_USERSET) {
6589 if (SQL_FASTUSERS) {
6590 authstr = pstrcat(tmp_pool, authstr, "userset(fast) ", NULL);
6591
6592 } else {
6593 authstr = pstrcat(tmp_pool, authstr, "userset ", NULL);
6594 }
6595 }
6596
6597 if (SQL_GROUPSET) {
6598 if (SQL_FASTGROUPS) {
6599 authstr = pstrcat(tmp_pool, authstr, "groupset(fast)", NULL);
6600
6601 } else {
6602 authstr = pstrcat(tmp_pool, authstr, "groupset", NULL);
6603 }
6604 }
6605
6606 sql_log(DEBUG_INFO, "authenticate : %s",
6607 (!authstr || *authstr=='\0') ? "off" : authstr);
6608
6609 if (SQL_USERS ||
6610 cmap.sql_fstor ||
6611 cmap.sql_frate) {
6612 sql_log(DEBUG_INFO, "usertable : %s", cmap.usrtable);
6613 sql_log(DEBUG_INFO, "userid field : %s", cmap.usrfield);
6614 }
6615
6616 if (SQL_USERS) {
6617 sql_log(DEBUG_INFO, "password field : %s", cmap.pwdfield);
6618
6619 sql_log(DEBUG_INFO, "UID field : %s",
6620 (cmap.uidfield ? cmap.uidfield : "NULL"));
6621
6622 sql_log(DEBUG_INFO, "GID field : %s",
6623 (cmap.gidfield ? cmap.gidfield : "NULL"));
6624
6625 if (cmap.homedirfield)
6626 sql_log(DEBUG_INFO, "homedir field : %s", cmap.homedirfield);
6627
6628 if (cmap.defaulthomedir)
6629 sql_log(DEBUG_INFO, "homedir(default) : '%s'", cmap.defaulthomedir);
6630
6631 sql_log(DEBUG_INFO, "shell field : %s",
6632 (cmap.shellfield ? cmap.shellfield : "NULL"));
6633 }
6634
6635 if (SQL_GROUPS) {
6636 sql_log(DEBUG_INFO, "group table : %s", cmap.grptable);
6637 sql_log(DEBUG_INFO, "groupname field : %s", cmap.grpfield);
6638 sql_log(DEBUG_INFO, "grp GID field : %s", cmap.grpgidfield);
6639 sql_log(DEBUG_INFO, "grp members field : %s", cmap.grpmembersfield);
6640 }
6641
6642 if (SQL_USERS) {
6643 sql_log(DEBUG_INFO, "SQLMinUserUID : %u", cmap.minuseruid);
6644 sql_log(DEBUG_INFO, "SQLMinUserGID : %u", cmap.minusergid);
6645 }
6646
6647 sql_log(DEBUG_INFO, "SQLDefaultUID : %u", cmap.defaultuid);
6648 sql_log(DEBUG_INFO, "SQLDefaultGID : %u", cmap.defaultgid);
6649
6650 if (cmap.sql_fstor) {
6651 sql_log(DEBUG_INFO, "sql_fstor : %s", cmap.sql_fstor);
6652 sql_log(DEBUG_INFO, "sql_fretr : %s", cmap.sql_fretr);
6653 sql_log(DEBUG_INFO, "sql_bstor : %s", cmap.sql_bstor);
6654 sql_log(DEBUG_INFO, "sql_bretr : %s", cmap.sql_bretr);
6655 }
6656
6657 if (cmap.sql_frate) {
6658 sql_log(DEBUG_INFO, "sql_frate : %s", cmap.sql_frate);
6659 sql_log(DEBUG_INFO, "sql_fcred : %s", cmap.sql_fcred);
6660 sql_log(DEBUG_INFO, "sql_brate : %s", cmap.sql_brate);
6661 sql_log(DEBUG_INFO, "sql_bcred : %s", cmap.sql_bcred);
6662 }
6663
6664 sql_log(DEBUG_FUNC, "%s", "<<< sql_sess_init");
6665
6666 destroy_pool(tmp_pool);
6667
6668 pr_event_register(&sql_module, "core.chroot", sql_chroot_ev, NULL);
6669 pr_event_register(&sql_module, "core.exit", sql_exit_ev, NULL);
6670
6671 c = find_config(main_server->conf, CONF_PARAM, "SQLKeepAlive", FALSE);
6672 if (c != NULL) {
6673 int interval;
6674
6675 interval = *((int *) c->argv[0]);
6676
6677 sql_keepalive_stmt = c->argv[1];
6678 sql_keepalive_timer_id = pr_timer_add(interval, -1, &sql_module,
6679 sql_keepalive_cb, "SQL keepalive");
6680 sql_log(DEBUG_INFO, "scheduling SQLKeepAlive (using '%s') every %d %s",
6681 sql_keepalive_stmt, interval, interval != 1 ? "secs" : "sec");
6682 }
6683
6684 return 0;
6685 }
6686
6687 /*****************************************************************
6688 *
6689 * HANDLER TABLES
6690 *
6691 *****************************************************************/
6692
6693 static conftable sql_conftab[] = {
6694 { "SQLAuthenticate", set_sqlauthenticate, NULL },
6695 { "SQLAuthTypes", set_sqlauthtypes, NULL },
6696 { "SQLBackend", set_sqlbackend, NULL },
6697 { "SQLConnectInfo", set_sqlconnectinfo, NULL },
6698 { "SQLDefaultGID", set_sqldefaultgid, NULL },
6699 { "SQLDefaultHomedir", set_sqldefaulthomedir, NULL },
6700 { "SQLDefaultUID", set_sqldefaultuid, NULL },
6701 { "SQLEngine", set_sqlengine, NULL },
6702 { "SQLGroupInfo", set_sqlgroupinfo, NULL },
6703 { "SQLGroupPrimaryKey", set_sqlgroupprimarykey, NULL },
6704 { "SQLGroupWhereClause", set_sqlgroupwhereclause, NULL },
6705 { "SQLKeepAlive", set_sqlkeepalive, NULL },
6706 { "SQLLog", set_sqllog, NULL },
6707 { "SQLLogFile", set_sqllogfile, NULL },
6708 { "SQLLogOnEvent", set_sqllogonevent, NULL },
6709 { "SQLMinID", set_sqlminid, NULL },
6710 { "SQLMinUserGID", set_sqlminusergid, NULL },
6711 { "SQLMinUserUID", set_sqlminuseruid, NULL },
6712 { "SQLNamedConnectInfo", set_sqlnamedconnectinfo, NULL },
6713 { "SQLNamedQuery", set_sqlnamedquery, NULL },
6714 { "SQLNegativeCache", set_sqlnegativecache, NULL },
6715 { "SQLOptions", set_sqloptions, NULL },
6716 { "SQLShowInfo", set_sqlshowinfo, NULL },
6717 { "SQLUserInfo", set_sqluserinfo, NULL },
6718 { "SQLUserPrimaryKey", set_sqluserprimarykey, NULL },
6719 { "SQLUserWhereClause", set_sqluserwhereclause, NULL },
6720
6721 { "SQLRatios", set_sqlratios, NULL },
6722 { "SQLRatioStats", set_sqlratiostats, NULL },
6723
6724 { NULL, NULL, NULL }
6725 };
6726
6727 static cmdtable sql_cmdtab[] = {
6728 { PRE_CMD, C_PASS, G_NONE, sql_pre_pass, FALSE, FALSE },
6729 { POST_CMD, C_PASS, G_NONE, sql_post_pass, FALSE, FALSE },
6730 { PRE_CMD, C_DELE, G_NONE, sql_pre_dele, FALSE, FALSE },
6731 { POST_CMD, C_RETR, G_NONE, sql_post_retr, FALSE, FALSE },
6732 { POST_CMD, C_STOR, G_NONE, sql_post_stor, FALSE, FALSE },
6733 { POST_CMD, C_ANY, G_NONE, info_master, FALSE, FALSE },
6734 { POST_CMD_ERR, C_ANY, G_NONE, errinfo_master, FALSE, FALSE },
6735 { LOG_CMD, C_ANY, G_NONE, log_master, FALSE, FALSE },
6736 { LOG_CMD_ERR, C_ANY, G_NONE, err_master, FALSE, FALSE },
6737
6738 /* Module hooks */
6739 { HOOK, "sql_change", G_NONE, sql_change, FALSE, FALSE },
6740 { HOOK, "sql_cleanup", G_NONE, sql_cleanup, FALSE, FALSE },
6741 { HOOK, "sql_close_conn", G_NONE, sql_closeconn, FALSE, FALSE },
6742 { HOOK, "sql_define_conn", G_NONE, sql_defineconn, FALSE, FALSE },
6743 { HOOK, "sql_escapestr", G_NONE, sql_escapestr, FALSE, FALSE },
6744 { HOOK, "sql_load_backend", G_NONE, sql_load_backend,FALSE, FALSE },
6745 { HOOK, "sql_lookup", G_NONE, sql_lookup, FALSE, FALSE },
6746 { HOOK, "sql_open_conn", G_NONE, sql_openconn, FALSE, FALSE },
6747 { HOOK, "sql_prepare", G_NONE, sql_prepare, FALSE, FALSE },
6748 { HOOK, "sql_select", G_NONE, sql_select, FALSE, FALSE },
6749
6750 { 0, NULL }
6751 };
6752
6753 static authtable sql_authtab[] = {
6754 { 0, "setpwent", sql_auth_setpwent },
6755 { 0, "getpwent", sql_auth_getpwent },
6756 { 0, "endpwent", sql_auth_endpwent },
6757 { 0, "setgrent", sql_auth_setgrent },
6758 { 0, "getgrent", sql_auth_getgrent },
6759 { 0, "endgrent", sql_auth_endgrent },
6760 { 0, "getpwnam", sql_auth_getpwnam },
6761 { 0, "getpwuid", sql_auth_getpwuid },
6762 { 0, "getgrnam", sql_auth_getgrnam },
6763 { 0, "getgrgid", sql_auth_getgrgid },
6764 { 0, "auth", sql_auth_authenticate },
6765 { 0, "check", sql_auth_check },
6766 { 0, "uid2name", sql_auth_uid2name },
6767 { 0, "gid2name", sql_auth_gid2name },
6768 { 0, "name2uid", sql_auth_name2uid },
6769 { 0, "name2gid", sql_auth_name2gid },
6770 { 0, "getgroups", sql_auth_getgroups },
6771
6772 /* Note: these should be HOOKs, and in the cmdtab. */
6773 { 0, "getstats", sql_getstats },
6774 { 0, "getratio", sql_getratio },
6775
6776 { 0, NULL, NULL }
6777 };
6778
6779 module sql_module = {
6780
6781 /* Always NULL */
6782 NULL, NULL,
6783
6784 /* Module API version */
6785 0x20,
6786
6787 /* Module name */
6788 "sql",
6789
6790 /* Module configuration directive table */
6791 sql_conftab,
6792
6793 /* Module command handler table */
6794 sql_cmdtab,
6795
6796 /* Module auth handler table */
6797 sql_authtab,
6798
6799 /* Module initialization */
6800 sql_init,
6801
6802 /* Session initialization */
6803 sql_sess_init,
6804
6805 /* Module version */
6806 MOD_SQL_VERSION
6807 };
6808