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