1 /*
2  * ProFTPD: mod_sftp_sql -- SQL backend module for retrieving authorized keys
3  * Copyright (c) 2008-2016 TJ Saunders
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18  *
19  * As a special exemption, TJ Saunders and other respective copyright holders
20  * give permission to link this program with OpenSSL, and distribute the
21  * resulting executable, without including the source code for OpenSSL in the
22  * source distribution.
23  *
24  * This is mod_sftp_sql, contrib software for proftpd 1.3.x and above.
25  * For more information contact TJ Saunders <tj@castaglia.org>.
26  */
27 
28 #include "conf.h"
29 #include "privs.h"
30 #include "mod_sftp.h"
31 #include "mod_sql.h"
32 
33 #define MOD_SFTP_SQL_VERSION		"mod_sftp_sql/0.4"
34 
35 module sftp_sql_module;
36 
37 #define SFTP_SQL_BUFSZ			1024
38 
39 struct sqlstore_key {
40   const char *subject;
41 
42   /* Key data */
43   unsigned char *key_data;
44   uint32_t key_datalen;
45 };
46 
47 struct sqlstore_data {
48   const char *select_query;
49 };
50 
51 static const char *trace_channel = "ssh2";
52 
sqlstore_cmd_create(pool * parent_pool,unsigned int argc,...)53 static cmd_rec *sqlstore_cmd_create(pool *parent_pool, unsigned int argc, ...) {
54   register unsigned int i = 0;
55   pool *cmd_pool = NULL;
56   cmd_rec *cmd = NULL;
57   va_list argp;
58 
59   cmd_pool = make_sub_pool(parent_pool);
60   cmd = (cmd_rec *) pcalloc(cmd_pool, sizeof(cmd_rec));
61   cmd->pool = cmd_pool;
62 
63   cmd->argc = argc;
64   cmd->argv = pcalloc(cmd->pool, argc * sizeof(void *));
65 
66   /* Hmmm... */
67   cmd->tmp_pool = cmd->pool;
68 
69   va_start(argp, argc);
70   for (i = 0; i < argc; i++) {
71     cmd->argv[i] = va_arg(argp, char *);
72   }
73   va_end(argp);
74 
75   return cmd;
76 }
77 
78 /* Given a blob of bytes retrieved from a single row, read that blob as if
79  * it were text, line by line.
80  */
sqlstore_getline(pool * p,char ** blob,size_t * bloblen)81 static char *sqlstore_getline(pool *p, char **blob, size_t *bloblen) {
82   char linebuf[SFTP_SQL_BUFSZ], *line = "", *data;
83   size_t datalen;
84 
85   data = *blob;
86   datalen = *bloblen;
87 
88   if (data == NULL ||
89       datalen == 0) {
90     errno = EOF;
91     return NULL;
92   }
93 
94   while (data != NULL && datalen > 0) {
95     char *ptr;
96     size_t delimlen, linelen;
97     int have_line_continuation = FALSE;
98 
99     pr_signals_handle();
100 
101     if (datalen <= 2) {
102       line = pstrcat(p, line, data, NULL);
103 
104       *blob = NULL;
105       *bloblen = 0;
106 
107       return line;
108     }
109 
110     /* Find the CRLF markers in the data. */
111     ptr = strstr(data, "\r\n");
112     if (ptr != NULL) {
113       delimlen = 1;
114 
115     } else {
116       ptr = strstr(data, "\n");
117       if (ptr != NULL) {
118         delimlen = 0;
119       }
120     }
121 
122     if (ptr == NULL) {
123       /* Just return the rest of the data. */
124       line = pstrcat(p, line, data, NULL);
125 
126       *blob = NULL;
127       *bloblen = 0;
128 
129       return line;
130     }
131 
132     linelen = (ptr - data + 1);
133 
134     if (linelen == 1) {
135       data += (delimlen + 1);
136       datalen -= (delimlen + 1);
137 
138       continue;
139     }
140 
141     /* Watch out for lines larger than our buffer. */
142     if (linelen > sizeof(linebuf)) {
143       (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION,
144         "line of key data (%lu bytes) exceeds buffer size, truncating; "
145         "this WILL cause authentication failures", (unsigned long) linelen);
146       linelen = sizeof(linebuf);
147     }
148 
149     memcpy(linebuf, data, linelen);
150     linebuf[linelen-1] = '\0';
151 
152     data += (linelen + delimlen);
153     datalen -= (linelen + delimlen);
154 
155     /* Check for continued lines. */
156     if (linelen >= 2 &&
157         linebuf[linelen-2] == '\\') {
158       linebuf[linelen-2] = '\0';
159       have_line_continuation = TRUE;
160     }
161 
162     line = pstrcat(p, line, linebuf, NULL);
163     linelen = strlen(line);
164 
165     if (have_line_continuation) {
166       continue;
167     }
168 
169     ptr = strchr(line, ':');
170     if (ptr != NULL) {
171       unsigned int header_taglen, header_valuelen;
172 
173       /* We have a header.  Make sure the header tag is not longer than
174        * the specified length of 64 bytes, and that the header value is
175        * not longer than 1024 bytes.
176        */
177       header_taglen = ptr - line;
178       if (header_taglen > 64) {
179         (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION,
180           "header tag too long (%u) in retrieved SQL data", header_taglen);
181         errno = EINVAL;
182         return NULL;
183       }
184 
185       /* Header value starts at 2 after the ':' (one for the mandatory
186        * space character.
187        */
188       header_valuelen = linelen - (header_taglen + 2);
189       if (header_valuelen > 1024) {
190         (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION,
191           "header value too long (%u) in retrieved SQL data", header_valuelen);
192         errno = EINVAL;
193         return NULL;
194       }
195     }
196 
197     *blob = data;
198     *bloblen = datalen;
199 
200     return line;
201   }
202 
203   return NULL;
204 }
205 
sqlstore_get_key_raw(pool * p,char ** blob,size_t * bloblen)206 static struct sqlstore_key *sqlstore_get_key_raw(pool *p, char **blob,
207     size_t *bloblen) {
208   char chunk[SFTP_SQL_BUFSZ], *data = NULL;
209   BIO *bio = NULL, *b64 = NULL, *bmem = NULL;
210   int chunklen;
211   long datalen = 0;
212   struct sqlstore_key *key = NULL;
213 
214   bio = BIO_new(BIO_s_mem());
215 
216   if (BIO_write(bio, (void *) *blob, *bloblen) < 0) {
217     (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION,
218       "error buffering base64 data");
219   }
220 
221   /* Add a base64 filter BIO, and read the data out, thus base64-decoding
222    * the key.  Write the decoded data into another memory BIO.
223    */
224   b64 = BIO_new(BIO_f_base64());
225   BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
226   bio = BIO_push(b64, bio);
227 
228   bmem = BIO_new(BIO_s_mem());
229 
230   memset(chunk, '\0', sizeof(chunk));
231   chunklen = BIO_read(bio, (void *) chunk, sizeof(chunk));
232 
233   if (chunklen < 0 &&
234       !BIO_should_retry(bio)) {
235     (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION,
236       "unable to base64-decode raw key data from database: %s",
237       sftp_crypto_get_errors());
238     BIO_free_all(bio);
239     BIO_free_all(bmem);
240 
241     errno = EPERM;
242     return NULL;
243   }
244 
245   while (chunklen > 0) {
246     pr_signals_handle();
247 
248     if (BIO_write(bmem, (void *) chunk, chunklen) < 0) {
249       (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION,
250         "error writing to memory BIO: %s", sftp_crypto_get_errors());
251       BIO_free_all(bio);
252       BIO_free_all(bmem);
253 
254       errno = EPERM;
255       return NULL;
256     }
257 
258     memset(chunk, '\0', sizeof(chunk));
259     chunklen = BIO_read(bio, (void *) chunk, sizeof(chunk));
260   }
261 
262   datalen = BIO_get_mem_data(bmem, &data);
263 
264   if (data != NULL &&
265       datalen > 0) {
266     key = pcalloc(p, sizeof(struct sqlstore_key));
267     key->key_data = pcalloc(p, datalen + 1);
268     key->key_datalen = datalen;
269     memcpy(key->key_data, data, datalen);
270 
271   } else {
272     (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION,
273       "error base64-decoding raw key data from database");
274   }
275 
276   BIO_free_all(bio);
277   bio = NULL;
278 
279   BIO_free_all(bmem);
280   return key;
281 }
282 
sqlstore_get_key_rfc4716(pool * p,char ** blob,size_t * bloblen)283 static struct sqlstore_key *sqlstore_get_key_rfc4716(pool *p, char **blob,
284     size_t *bloblen) {
285   char *line;
286   BIO *bio = NULL;
287   struct sqlstore_key *key = NULL;
288   size_t begin_markerlen = 0, end_markerlen = 0;
289 
290   line = sqlstore_getline(p, blob, bloblen);
291   while (line == NULL &&
292          errno == EINVAL) {
293     pr_signals_handle();
294     line = sqlstore_getline(p, blob, bloblen);
295   }
296 
297   if (line == NULL) {
298     return NULL;
299   }
300 
301   begin_markerlen = strlen(SFTP_SSH2_PUBKEY_BEGIN_MARKER);
302   end_markerlen = strlen(SFTP_SSH2_PUBKEY_END_MARKER);
303 
304   while (line != NULL) {
305     pr_signals_handle();
306 
307     if (key == NULL &&
308         strncmp(line, SFTP_SSH2_PUBKEY_BEGIN_MARKER, begin_markerlen) == 0) {
309       key = pcalloc(p, sizeof(struct sqlstore_key));
310       bio = BIO_new(BIO_s_mem());
311 
312     } else if (key != NULL &&
313                strncmp(line, SFTP_SSH2_PUBKEY_END_MARKER, end_markerlen) == 0) {
314       if (bio != NULL) {
315         char chunk[SFTP_SQL_BUFSZ], *data = NULL;
316         BIO *b64 = NULL, *bmem = NULL;
317         int chunklen;
318         long datalen = 0;
319 
320         /* Add a base64 filter BIO, and read the data out, thus base64-decoding
321          * the key.  Write the decoded data into another memory BIO.
322          */
323         b64 = BIO_new(BIO_f_base64());
324         BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
325         bio = BIO_push(b64, bio);
326 
327         bmem = BIO_new(BIO_s_mem());
328 
329         memset(chunk, '\0', sizeof(chunk));
330         chunklen = BIO_read(bio, (void *) chunk, sizeof(chunk));
331 
332         if (chunklen < 0 &&
333             !BIO_should_retry(bio)) {
334           (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION,
335             "unable to base64-decode RFC4716 key data from database: %s",
336           sftp_crypto_get_errors());
337           BIO_free_all(bio);
338           BIO_free_all(bmem);
339 
340           errno = EPERM;
341           return NULL;
342         }
343 
344         while (chunklen > 0) {
345           pr_signals_handle();
346 
347           if (BIO_write(bmem, (void *) chunk, chunklen) < 0) {
348             (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION,
349               "error writing to memory BIO: %s", sftp_crypto_get_errors());
350             BIO_free_all(bio);
351             BIO_free_all(bmem);
352 
353             errno = EPERM;
354             return NULL;
355           }
356 
357           memset(chunk, '\0', sizeof(chunk));
358           chunklen = BIO_read(bio, (void *) chunk, sizeof(chunk));
359         }
360 
361         datalen = BIO_get_mem_data(bmem, &data);
362 
363         if (data != NULL &&
364             datalen > 0) {
365           key = pcalloc(p, sizeof(struct sqlstore_key));
366           key->key_data = pcalloc(p, datalen + 1);
367           key->key_datalen = datalen;
368           memcpy(key->key_data, data, datalen);
369 
370         } else {
371           (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION,
372             "error base64-decoding RFC4716 key data from database");
373         }
374 
375         BIO_free_all(bio);
376         bio = NULL;
377 
378         BIO_free_all(bmem);
379       }
380 
381       break;
382 
383     } else {
384       if (key) {
385         if (strstr(line, ": ") != NULL) {
386           if (strncasecmp(line, "Subject: ", 9) == 0) {
387             key->subject = pstrdup(p, line + 9);
388           }
389 
390         } else {
391           if (BIO_write(bio, line, strlen(line)) < 0) {
392             (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION,
393               "error buffering base64 data");
394           }
395         }
396       }
397     }
398 
399     line = sqlstore_getline(p, blob, bloblen);
400     while (line == NULL &&
401            errno == EINVAL) {
402       pr_signals_handle();
403       line = sqlstore_getline(p, blob, bloblen);
404     }
405   }
406 
407   return key;
408 }
409 
sqlstore_get_str(pool * p,char * str)410 static char *sqlstore_get_str(pool *p, char *str) {
411   cmdtable *cmdtab;
412   cmd_rec *cmd;
413   modret_t *res;
414 
415   if (strlen(str) == 0)
416     return str;
417 
418   /* Find the cmdtable for the sql_escapestr command. */
419   cmdtab = pr_stash_get_symbol2(PR_SYM_HOOK, "sql_escapestr", NULL, NULL, NULL);
420   if (cmdtab == NULL) {
421     (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION,
422       "unable to find SQL hook symbol 'sql_escapestr'");
423     return str;
424   }
425 
426   cmd = sqlstore_cmd_create(p, 1, pr_str_strip(p, str));
427 
428   /* Call the handler. */
429   res = pr_module_call(cmdtab->m, cmdtab->handler, cmd);
430 
431   /* Check the results. */
432   if (MODRET_ISERROR(res)) {
433     (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION,
434       "error executing 'sql_escapestring'");
435     return str;
436   }
437 
438   return res->data;
439 }
440 
sqlstore_verify_key_raw(pool * p,struct sqlstore_data * store_data,int nrow,char * col_data,size_t col_datalen,unsigned char * key_data,uint32_t key_datalen)441 static int sqlstore_verify_key_raw(pool *p, struct sqlstore_data *store_data,
442     int nrow, char *col_data, size_t col_datalen, unsigned char *key_data,
443     uint32_t key_datalen) {
444   struct sqlstore_key *key;
445   int res;
446 
447   key = sqlstore_get_key_raw(p, &col_data, &col_datalen);
448   if (key == NULL) {
449     pr_trace_msg(trace_channel, 10,
450       "unable to parse data (row %u) as raw data", nrow+1);
451     return -1;
452   }
453 
454   res = sftp_keys_compare_keys(p, key_data, key_datalen, key->key_data,
455     key->key_datalen);
456   if (res < 0) {
457     (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION,
458       "error comparing client-sent host key with SQL data (row %u) from "
459       "SQLNamedQuery '%s': %s", nrow+1, store_data->select_query,
460       strerror(errno));
461 
462   } else if (res == FALSE) {
463     (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION,
464       "client-sent host key does not match SQL data (row %u) from "
465       "SQLNamedQuery '%s'", nrow+1, store_data->select_query);
466     res = -1;
467 
468   } else {
469     res = 0;
470   }
471 
472   return res;
473 }
474 
sqlstore_verify_key_rfc4716(pool * p,struct sqlstore_data * store_data,int nrow,char * col_data,size_t col_datalen,unsigned char * key_data,uint32_t key_datalen)475 static int sqlstore_verify_key_rfc4716(pool *p,
476     struct sqlstore_data *store_data, int nrow, char *col_data,
477     size_t col_datalen, unsigned char *key_data, uint32_t key_datalen) {
478   struct sqlstore_key *key;
479   int res;
480 
481   key = sqlstore_get_key_rfc4716(p, &col_data, &col_datalen);
482   while (key != NULL) {
483     pr_signals_handle();
484 
485     res = sftp_keys_compare_keys(p, key_data, key_datalen, key->key_data,
486       key->key_datalen);
487     if (res < 0) {
488       (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION,
489         "error comparing client-sent key with SQL data (row %u) from "
490         "SQLNamedQuery '%s': %s", nrow+1, store_data->select_query,
491         strerror(errno));
492       key = sqlstore_get_key_rfc4716(p, &col_data, &col_datalen);
493       continue;
494 
495     } else if (res == FALSE) {
496       (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION,
497         "client-sent key does not match SQL data (row %u) from "
498         "SQLNamedQuery '%s'", nrow+1, store_data->select_query);
499       key = sqlstore_get_key_rfc4716(p, &col_data, &col_datalen);
500       continue;
501     }
502 
503     return 0;
504   }
505 
506   return -1;
507 }
508 
sqlstore_verify_host_key(sftp_keystore_t * store,pool * p,const char * user,const char * host_fqdn,const char * host_user,unsigned char * key_data,uint32_t key_datalen)509 static int sqlstore_verify_host_key(sftp_keystore_t *store, pool *p,
510     const char *user, const char *host_fqdn, const char *host_user,
511     unsigned char *key_data, uint32_t key_datalen) {
512   register unsigned int i;
513   struct sqlstore_data *store_data;
514   pool *tmp_pool;
515   cmdtable *sql_cmdtab;
516   cmd_rec *sql_cmd;
517   modret_t *sql_res;
518   array_header *sql_data;
519   char **values;
520   int res;
521 
522   store_data = store->keystore_data;
523 
524   /* Find the cmdtable for the sql_lookup command. */
525   sql_cmdtab = pr_stash_get_symbol2(PR_SYM_HOOK, "sql_lookup", NULL, NULL,
526     NULL);
527   if (sql_cmdtab == NULL) {
528     (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION,
529       "unable to find SQL hook symbol 'sql_lookup'");
530     errno = EPERM;
531     return -1;
532   }
533 
534   tmp_pool = make_sub_pool(store->keystore_pool);
535 
536   /* Prepare the SELECT query. */
537   sql_cmd = sqlstore_cmd_create(tmp_pool, 3, "sql_lookup",
538     store_data->select_query, sqlstore_get_str(tmp_pool, (char *) host_fqdn));
539 
540   /* Call the handler. */
541   sql_res = pr_module_call(sql_cmdtab->m, sql_cmdtab->handler, sql_cmd);
542   if (sql_res == NULL ||
543       MODRET_ISERROR(sql_res)) {
544     (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION,
545       "error processing SQLNamedQuery '%s'", store_data->select_query);
546     destroy_pool(tmp_pool);
547 
548     errno = EPERM;
549     return -1;
550   }
551 
552   sql_data = (array_header *) sql_res->data;
553 
554   if (sql_data->nelts == 0) {
555     (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION,
556       "SQLNamedQuery '%s' returned zero results", store_data->select_query);
557     destroy_pool(tmp_pool);
558     errno = ENOENT;
559     return -1;
560 
561   } else {
562     (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION,
563       "SQLNamedQuery '%s' returned %d %s", store_data->select_query,
564       sql_data->nelts, sql_data->nelts != 1 ? "rows" : "row");
565   }
566 
567   values = (char **) sql_data->elts;
568   for (i = 0; i < sql_data->nelts; i++) {
569     char *col_data;
570     size_t col_datalen;
571 
572     pr_signals_handle();
573 
574     col_data = values[i];
575     col_datalen = strlen(values[i]);
576 
577     res = sqlstore_verify_key_rfc4716(p, store_data, i, col_data, col_datalen,
578       key_data, key_datalen);
579     if (res == 0) {
580       pr_trace_msg(trace_channel, 10, "found matching RFC4716 public key "
581         "(row %u) for host '%s' using SQLNamedQuery '%s'", i+1, host_fqdn,
582         store_data->select_query);
583       destroy_pool(tmp_pool);
584       return 0;
585     }
586 
587     res = sqlstore_verify_key_raw(p, store_data, i, col_data, col_datalen,
588       key_data, key_datalen);
589     if (res == 0) {
590       pr_trace_msg(trace_channel, 10, "found matching public key (row %u) for "
591         "host '%s' using SQLNamedQuery '%s'", i+1, host_fqdn,
592         store_data->select_query);
593       destroy_pool(tmp_pool);
594       return 0;
595     }
596   }
597 
598   destroy_pool(tmp_pool);
599   errno = ENOENT;
600   return -1;
601 }
602 
sqlstore_verify_user_key(sftp_keystore_t * store,pool * p,const char * user,unsigned char * key_data,uint32_t key_datalen)603 static int sqlstore_verify_user_key(sftp_keystore_t *store, pool *p,
604     const char *user, unsigned char *key_data, uint32_t key_datalen) {
605   register unsigned int i;
606   struct sqlstore_data *store_data;
607   pool *tmp_pool;
608   cmdtable *sql_cmdtab;
609   cmd_rec *sql_cmd;
610   modret_t *sql_res;
611   array_header *sql_data;
612   char **values;
613 
614   store_data = store->keystore_data;
615 
616   /* Find the cmdtable for the sql_lookup command. */
617   sql_cmdtab = pr_stash_get_symbol2(PR_SYM_HOOK, "sql_lookup", NULL, NULL,
618     NULL);
619   if (sql_cmdtab == NULL) {
620     (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION,
621       "unable to find SQL hook symbol 'sql_lookup'");
622     errno = EPERM;
623     return -1;
624   }
625 
626   tmp_pool = make_sub_pool(store->keystore_pool);
627 
628   /* Prepare the SELECT query. */
629   sql_cmd = sqlstore_cmd_create(tmp_pool, 3, "sql_lookup",
630     store_data->select_query, sqlstore_get_str(tmp_pool, (char *) user));
631 
632   /* Call the handler. */
633   sql_res = pr_module_call(sql_cmdtab->m, sql_cmdtab->handler, sql_cmd);
634   if (sql_res == NULL ||
635       MODRET_ISERROR(sql_res)) {
636     (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION,
637       "error processing SQLNamedQuery '%s'", store_data->select_query);
638     destroy_pool(tmp_pool);
639 
640     errno = EPERM;
641     return -1;
642   }
643 
644   sql_data = (array_header *) sql_res->data;
645 
646   if (sql_data->nelts == 0) {
647     (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION,
648       "SQLNamedQuery '%s' returned zero results", store_data->select_query);
649     destroy_pool(tmp_pool);
650     errno = ENOENT;
651     return -1;
652   }
653 
654   (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION,
655     "SQLNamedQuery '%s' returned %d %s", store_data->select_query,
656     sql_data->nelts, sql_data->nelts != 1 ? "rows" : "row");
657 
658   values = (char **) sql_data->elts;
659   for (i = 0; i < sql_data->nelts; i++) {
660     int res;
661     char *col_data;
662     size_t col_datalen;
663 
664     pr_signals_handle();
665 
666     col_data = values[i];
667     col_datalen = strlen(values[i]);
668 
669     res = sqlstore_verify_key_rfc4716(p, store_data, i, col_data, col_datalen,
670       key_data, key_datalen);
671     if (res == 0) {
672       pr_trace_msg(trace_channel, 10, "found matching RFC4716 public key "
673         "(row %u) for user '%s' using SQLNamedQuery '%s'", i+1, user,
674         store_data->select_query);
675       destroy_pool(tmp_pool);
676       return 0;
677     }
678 
679     res = sqlstore_verify_key_raw(p, store_data, i, col_data, col_datalen,
680       key_data, key_datalen);
681     if (res == 0) {
682       pr_trace_msg(trace_channel, 10, "found matching public key (row %u) for "
683         "user '%s' using SQLNamedQuery '%s'", i+1, user,
684         store_data->select_query);
685       destroy_pool(tmp_pool);
686       return 0;
687     }
688   }
689 
690   destroy_pool(tmp_pool);
691   errno = ENOENT;
692   return -1;
693 }
694 
sqlstore_close(sftp_keystore_t * store)695 static int sqlstore_close(sftp_keystore_t *store) {
696   /* Nothing really to do here. */
697   return 0;
698 }
699 
sqlstore_open(pool * parent_pool,int requested_key_type,const char * store_info,const char * user)700 static sftp_keystore_t *sqlstore_open(pool *parent_pool,
701     int requested_key_type, const char *store_info, const char *user) {
702   sftp_keystore_t *store;
703   pool *sqlstore_pool, *tmp_pool;
704   struct sqlstore_data *store_data;
705   char *named_query, *select_query, *ptr;
706   config_rec *c;
707 
708   tmp_pool = make_sub_pool(parent_pool);
709 
710   sqlstore_pool = make_sub_pool(parent_pool);
711   pr_pool_tag(sqlstore_pool, "SFTP SQL-based Keystore Pool");
712 
713   store = pcalloc(sqlstore_pool, sizeof(sftp_keystore_t));
714   store->keystore_pool = sqlstore_pool;
715   store->store_ktypes = requested_key_type;
716 
717   switch (requested_key_type) {
718     case SFTP_SSH2_HOST_KEY_STORE:
719       store->verify_host_key = sqlstore_verify_host_key;
720       break;
721 
722     case SFTP_SSH2_USER_KEY_STORE:
723       store->verify_user_key = sqlstore_verify_user_key;
724       break;
725   }
726 
727   store->store_close = sqlstore_close;
728 
729   /* Parse the SELECT query name out of the store_info string:
730    *
731    *  "/<select-named-query"
732    */
733   ptr = strchr(store_info, '/');
734   if (ptr == NULL) {
735     (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION,
736       "badly formatted store info '%s'", store_info);
737     destroy_pool(tmp_pool);
738 
739     errno = EINVAL;
740     return NULL;
741   }
742 
743   ptr++;
744   select_query = pstrdup(sqlstore_pool, ptr);
745 
746   /* Verify that the named query has indeed been configured.  This is based
747    * on how mod_sql creates its config_rec names.
748    */
749   named_query = pstrcat(tmp_pool, "SQLNamedQuery_", select_query, NULL);
750 
751   c = find_config(main_server->conf, CONF_PARAM, named_query, FALSE);
752   if (c == NULL) {
753     (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
754       "unable to resolve SQLNamedQuery '%s'", select_query);
755     destroy_pool(tmp_pool);
756 
757     errno = EINVAL;
758     return NULL;
759   }
760 
761   store_data = pcalloc(sqlstore_pool, sizeof(struct sqlstore_data));
762   store->keystore_data = store_data;
763   store_data->select_query = pstrdup(sqlstore_pool, select_query);
764 
765   destroy_pool(tmp_pool);
766   return store;
767 }
768 
769 /* Event Handlers
770  */
771 
772 #if defined(PR_SHARED_MODULE)
sftpsql_mod_unload_ev(const void * event_data,void * user_data)773 static void sftpsql_mod_unload_ev(const void *event_data, void *user_data) {
774   if (strcmp("mod_sftp_sql.c", (const char *) event_data) == 0) {
775     /* XXX any further cleanup here */
776 
777     sftp_keystore_unregister_store("sql",
778       SFTP_SSH2_HOST_KEY_STORE|SFTP_SSH2_USER_KEY_STORE);
779     pr_event_unregister(&sftp_sql_module, NULL, NULL);
780   }
781 }
782 #endif /* !PR_SHARED_MODULE */
783 
784 /* Initialization functions
785  */
786 
sftpsql_init(void)787 static int sftpsql_init(void) {
788 
789   sftp_keystore_register_store("sql", sqlstore_open,
790     SFTP_SSH2_HOST_KEY_STORE|SFTP_SSH2_USER_KEY_STORE);
791 
792 #if defined(PR_SHARED_MODULE)
793   pr_event_register(&sftp_sql_module, "core.module-unload",
794     sftpsql_mod_unload_ev, NULL);
795 #endif /* !PR_SHARED_MODULE */
796 
797   return 0;
798 }
799 
800 module sftp_sql_module = {
801   NULL, NULL,
802 
803   /* Module API version 2.0 */
804   0x20,
805 
806   /* Module name */
807   "sftp_sql",
808 
809   /* Module configuration handler table */
810   NULL,
811 
812   /* Module command handler table */
813   NULL,
814 
815   /* Module authentication handler table */
816   NULL,
817 
818   /* Module initialization function */
819   sftpsql_init,
820 
821   /* Session initialization function */
822   NULL,
823 
824   /* Module version */
825   MOD_SFTP_SQL_VERSION
826 };
827