Move AFS vmail tokens to preauthuserdbcommon.c.
[hcoop/debian/courier-authlib.git] / authmysqllib.c
CommitLineData
d9898ee8 1/*
2** Copyright 2000-2005 Double Precision, Inc. See COPYING for
3** distribution information.
4*/
5
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9#include <unistd.h>
10#include <ctype.h>
11#include <sys/types.h>
12#include <sys/stat.h>
13#include <mysql.h>
14#include <time.h>
15
16#include "authmysql.h"
17#include "authmysqlrc.h"
18#include "auth.h"
19#include "courierauthdebug.h"
20
21#define err courier_auth_err
22
23/* siefca@pld.org.pl */
24#define MAX_SUBSTITUTION_LEN 32
25#define SV_BEGIN_MARK "$("
26#define SV_END_MARK ")"
27#define SV_BEGIN_LEN ((sizeof(SV_BEGIN_MARK))-1)
28#define SV_END_LEN ((sizeof(SV_END_MARK))-1)
29
64ff59ba 30static const char rcsid[]="$Id: authmysqllib.c,v 1.45 2007/10/07 02:50:45 mrsam Exp $";
d9898ee8 31
32/* siefca@pld.org.pl */
33struct var_data {
34 const char *name;
35 const char *value;
36 const size_t size;
37 size_t value_length;
38 } ;
39
40/* siefca@pld.org.pl */
41typedef int (*parsefunc)(const char *, size_t, void *);
42
43static const char *read_env(const char *env)
44{
45static char *mysqlauth=0;
46static size_t mysqlauth_size=0;
47size_t i;
48char *p=0;
49int l=strlen(env);
50
51 if (!mysqlauth)
52 {
53 FILE *f=fopen(AUTHMYSQLRC, "r");
54 struct stat buf;
55
56 if (!f) return (0);
57 if (fstat(fileno(f), &buf) ||
58 (mysqlauth=malloc(buf.st_size+2)) == 0)
59 {
60 fclose(f);
61 return (0);
62 }
63 if (fread(mysqlauth, buf.st_size, 1, f) != 1)
64 {
65 free(mysqlauth);
66 mysqlauth=0;
67 fclose(f);
68 return (0);
69 }
70 mysqlauth[mysqlauth_size=buf.st_size]=0;
71
72 for (i=0; i<mysqlauth_size; i++)
73 if (mysqlauth[i] == '\n')
74 { /* siefca@pld.org.pl */
75 if (!i || mysqlauth[i-1] != '\\')
76 {
77 mysqlauth[i]='\0';
78 }
79 else
80 {
81 mysqlauth[i]=mysqlauth[i-1]= ' ';
82 }
83 }
84 fclose(f);
85 }
86
87 for (i=0; i<mysqlauth_size; )
88 {
89 p=mysqlauth+i;
90 if (memcmp(p, env, l) == 0 &&
91 isspace((int)(unsigned char)p[l]))
92 {
93 p += l;
94 while (*p && *p != '\n' &&
95 isspace((int)(unsigned char)*p))
96 ++p;
97 break;
98 }
99
100 while (i < mysqlauth_size)
101 if (mysqlauth[i++] == 0) break;
102 }
103
104 if (i < mysqlauth_size)
105 return (p);
106 return (0);
107}
108
109static MYSQL mysql_buf;
110
111static MYSQL *mysql=0;
112
113static int do_connect()
114{
115const char *server;
116const char *userid;
117const char *password;
118const char *database;
119const char *server_socket=0;
120unsigned int server_port=0;
121unsigned int server_opt=0;
122const char *p;
123
124const char *sslkey;
125const char *sslcert;
126const char *sslcacert;
127const char *sslcapath;
128const char *sslcipher;
129unsigned int use_ssl=0;
130
131/*
132** Periodically detect dead connections.
133*/
134 if (mysql)
135 {
136 static time_t last_time=0;
137 time_t t_check;
138
139 time(&t_check);
140
141 if (t_check < last_time)
142 last_time=t_check; /* System clock changed */
143
144 if (t_check < last_time + 60)
145 return (0);
146
147 last_time=t_check;
148
149 if (mysql_ping(mysql) == 0) return (0);
150
151 DPRINTF("authmysqllib: mysql_ping failed, connection lost");
152 mysql_close(mysql);
153 mysql=0;
154 }
155
156 server=read_env("MYSQL_SERVER");
157 userid=read_env("MYSQL_USERNAME");
158 password=read_env("MYSQL_PASSWORD");
159 database=read_env("MYSQL_DATABASE");
160
161#if MYSQL_VERSION_ID >= 32200
162 sslkey=read_env("MYSQL_SSL_KEY");
163 sslcert=read_env("MYSQL_SSL_CERT");
164 sslcacert=read_env("MYSQL_SSL_CACERT");
165 sslcapath=read_env("MYSQL_SSL_CAPATH");
166 sslcipher=read_env("MYSQL_SSL_CIPHER");
167
168 if ((sslcert != NULL) && ((sslcacert != NULL) || (sslcapath != NULL)))
169 {
170 use_ssl=1;
171 }
172#endif
173
174 server_socket=(char *) read_env("MYSQL_SOCKET");
175
176 if ((p=read_env("MYSQL_PORT")) != 0)
177 {
178 server_port=(unsigned int) atoi(p);
179 }
180
181 if ((p=read_env("MYSQL_OPT")) != 0)
182 {
183 server_opt=(unsigned int) atol(p);
184 }
185
186 if (!server && !server_socket)
187 {
188 err("authmysql: MYSQL_SERVER nor MYSQL_SOCKET set in"
189 AUTHMYSQLRC ".");
190 return (-1);
191 }
192
193 if (!userid)
194 {
195 err("authmysql: MYSQL_USERNAME not set in "
196 AUTHMYSQLRC ".");
197 return (-1);
198 }
199
200 if (!database)
201 {
202 err("authmysql: MYSQL_DATABASE not set in "
203 AUTHMYSQLRC ".");
204 return (-1);
205 }
206
207#if MYSQL_VERSION_ID >= 32200
208 mysql_init(&mysql_buf);
209 if (use_ssl)
210 {
211 mysql_ssl_set(&mysql_buf, sslkey, sslcert, sslcacert,
212 sslcapath, sslcipher);
213 }
214 mysql=mysql_real_connect(&mysql_buf, server, userid, password,
215 NULL,
216 server_port,
217 server_socket,
218 server_opt);
219#else
220 mysql=mysql_connect(&mysql_buf, server, userid, password);
221#endif
222 if (!mysql)
223 {
224 err("failed to connect to mysql server (server=%s, userid=%s): %s",
225 server ? server : "<null>",
226 userid ? userid : "<null>",
227 mysql_error(&mysql_buf));
228 return (-1);
229 }
230
231 if (mysql_select_db(mysql, database))
232 {
233 err("authmysql: mysql_select_db(%s) error: %s",
234 database, mysql_error(mysql));
235 mysql_close(mysql);
236 mysql=0;
237 return (-1);
238 }
239 return (0);
240}
241
242void auth_mysql_cleanup()
243{
244 if (mysql)
245 {
246 mysql_close(mysql);
247 mysql=0;
248 }
249}
250
251static struct authmysqluserinfo ui={0, 0, 0, 0, 0, 0, 0, 0};
252
253static void initui()
254{
255
256 if (ui.username)
257 free(ui.username);
258 if (ui.cryptpw)
259 free(ui.cryptpw);
260 if (ui.clearpw)
261 free(ui.clearpw);
262 if (ui.home)
263 free(ui.home);
264 if (ui.maildir)
265 free(ui.maildir);
266 if (ui.quota)
267 free(ui.quota);
268 if (ui.fullname)
269 free(ui.fullname);
270 if (ui.options)
271 free(ui.options);
272 memset(&ui, 0, sizeof(ui));
273}
274
275static void append_username(char *p, const char *username,
276 const char *defdomain)
277{
278 for (strcpy(p, username); *p; p++)
279 if (*p == '\'' || *p == '"' || *p == '\\' ||
280 (int)(unsigned char)*p < ' ')
281 *p=' '; /* No funny business */
282 if (strchr(username, '@') == 0 && defdomain && *defdomain)
283 strcat(strcpy(p, "@"), defdomain);
284}
285
286/* siefca@pld.org.pl */
287static struct var_data *get_variable (const char *begin, size_t len,
288 struct var_data *vdt)
289{
290struct var_data *vdp;
291
292 if (!begin || !vdt) /* should never happend */
293 {
294 err("authmysql: critical error while "
295 "parsing substitution variable");
296 return NULL;
297 }
298 if (len < 1)
299 {
300 err("authmysql: unknown empty substitution "
301 "variable - aborting");
302 return NULL;
303 }
304 if (len > MAX_SUBSTITUTION_LEN)
305 {
306 err("authmysql: variable name too long "
307 "while parsing substitution. "
308 "name begins with "
309 SV_BEGIN_MARK
310 "%.*s...", MAX_SUBSTITUTION_LEN, begin);
311 return NULL;
312 }
313
314 for (vdp=vdt; vdp->name; vdp++)
315 if (vdp->size == len+1 &&
316 !strncmp(begin, vdp->name, len))
317 {
318 if (!vdp->value)
319 vdp->value = "";
320 if (!vdp->value_length) /* length cache */
321 vdp->value_length = strlen (vdp->value);
322 return vdp;
323 }
324
325 err("authmysql: unknown substitution variable "
326 SV_BEGIN_MARK
327 "%.*s"
328 SV_END_MARK
329 , (int)len, begin);
330
331 return NULL;
332}
333
334/* siefca@pld.org.pl */
335static int ParsePlugin_counter (const char *p, size_t length, void *vp)
336{
337 if (!p || !vp || length < 0)
338 {
339 err("authmysql: bad arguments while counting "
340 "query string");
341 return -1;
342 }
343
344 *((size_t *)vp) += length;
345
346 return 0;
347}
348
349/* siefca@pld.org.pl */
350static int ParsePlugin_builder (const char *p, size_t length, void *vp)
351{
352char **strptr = (char **) vp;
353
354 if (!p || !vp || length < 0)
355 {
356 err("authmysql: bad arguments while building "
357 "query string");
358 return -1;
359 }
360
361 if (!length) return 0;
362 memcpy ((void *) *strptr, (void *) p, length);
363 *strptr += length;
364
365 return 0;
366}
367
368/* siefca@pld.org.pl */
369static int parse_core (const char *source, struct var_data *vdt,
370 parsefunc outfn, void *result)
371{
372size_t v_size = 0,
373 t_size = 0;
374const char *p, *q, *e,
375 *v_begin, *v_end,
376 *t_begin, *t_end;
377struct var_data *v_ptr;
378
379 if (!source)
380 source = "";
381 if (!result)
382 {
383 err("authmysql: no memory allocated for result "
384 "while parser core was invoked");
385 return -1;
386 }
387 if (!vdt)
388 {
389 err("authmysql: no substitution table found "
390 "while parser core was invoked");
391 return -1;
392 }
393
394 q = source;
395 while ( (p=strstr(q, SV_BEGIN_MARK)) )
396 {
397 e = strstr (p, SV_END_MARK);
398 if (!e)
399 {
400 err("authmysql: syntax error in "
401 "substitution "
402 "- no closing symbol found! "
403 "bad variable begins with:"
404 "%.*s...", MAX_SUBSTITUTION_LEN, p);
405 return -1;
406 }
407
408 /*
409 **
410 ** __________sometext$(variable_name)_________
411 ** | | | |
412 ** t_begin' t_end' `v_begin `v_end
413 **
414 */
415
416 v_begin = p+SV_BEGIN_LEN; /* variable field ptr */
417 v_end = e-SV_END_LEN; /* variable field last character */
418 v_size = v_end-v_begin+1;/* variable field length */
419
420 t_begin = q; /* text field ptr */
421 t_end = p-1; /* text field last character */
422 t_size = t_end-t_begin+1;/* text field length */
423
424 /* work on text */
425 if ( (outfn (t_begin, t_size, result)) == -1 )
426 return -1;
427
428 /* work on variable */
429 v_ptr = get_variable (v_begin, v_size, vdt);
430 if (!v_ptr) return -1;
431
432 if ( (outfn (v_ptr->value, v_ptr->value_length, result)) == -1 )
433 return -1;
434
435 q = e + 1;
436 }
437
438 /* work on last part of text if any */
439 if (*q != '\0')
440 if ( (outfn (q, strlen(q), result)) == -1 )
441 return -1;
442
443 return 0;
444}
445
446/* siefca@pld.org.pl */
447static char *parse_string (const char *source, struct var_data *vdt)
448{
449struct var_data *vdp = NULL;
450char *output_buf = NULL,
451 *pass_buf = NULL;
452size_t buf_size = 2;
453
454 if (source == NULL || *source == '\0' ||
455 vdt == NULL || vdt[0].name == NULL)
456 {
457 err("authmysql: source clause is empty "
458 "- this is critical error");
459 return NULL;
460 }
461
462 /* zero var_data length cache - important! */
463 for (vdp=vdt; vdp->name; vdp++)
464 vdp->value_length = 0;
465
466
467 /* phase 1 - count and validate string */
468 if ( (parse_core (source, vdt, &ParsePlugin_counter, &buf_size)) != 0)
469 return NULL;
470
471 /* phase 2 - allocate memory */
472 output_buf = malloc (buf_size);
473 if (!output_buf)
474 {
475 perror ("malloc");
476 return NULL;
477 }
478 pass_buf = output_buf;
479
480 /* phase 3 - build the output string */
481 if ( (parse_core (source, vdt, &ParsePlugin_builder, &pass_buf)) != 0)
482 {
483 free (output_buf);
484 return NULL;
485 }
486 *pass_buf = '\0';
487
488 return output_buf;
489}
490
491/* siefca@pld.org.pl */
492static const char *get_localpart (const char *username)
493{
494size_t lbuf = 0;
495const char *l_end, *p;
496char *q;
497static char localpart_buf[130];
498
499 if (!username || *username == '\0') return NULL;
500
501 p = strchr(username,'@');
502 if (p)
503 {
504 if ((p-username) > 128)
505 return NULL;
506 l_end = p;
507 }
508 else
509 {
510 if ((lbuf = strlen(username)) > 128)
511 return NULL;
512 l_end = username + lbuf;
513 }
514
515 p=username;
516 q=localpart_buf;
517
518 while (*p && p != l_end)
519 if (*p == '\"' || *p == '\\' ||
520 *p == '\'' || (int)(unsigned char)*p < ' ')
521 p++;
522 else
523 *q++ = *p++;
524
525 *q = '\0';
526 return localpart_buf;
527}
528
529/* siefca@pld.org.pl */
530static const char *get_domain (const char *username, const char *defdomain)
531{
532static char domain_buf[260];
533const char *p;
534char *q;
535
536 if (!username || *username == '\0') return NULL;
537 p = strchr(username,'@');
538
539 if (!p || *(p+1) == '\0')
540 {
541 if (defdomain && *defdomain)
542 return defdomain;
543 else
544 return NULL;
545 }
546
547 p++;
548 if ((strlen(p)) > 256)
549 return NULL;
550
551 q = domain_buf;
552 while (*p)
553 if (*p == '\"' || *p == '\\' ||
554 *p == '\'' || (int)(unsigned char)*p < ' ')
555 p++;
556 else
557 *q++ = *p++;
558
559 *q = '\0';
560 return domain_buf;
561}
562
563/* siefca@pld.org.pl */
564
565static const char *validateMyPassword (const char *password)
566{
567static char pass_buf[2][540]; /* Use two buffers, see parse_chpass_clause */
568static int next_pass=0;
569const char *p;
570char *q, *endq;
571
572 if (!password || *password == '\0' || (strlen(password)) > 256)
573 return NULL;
574
575 next_pass= 1-next_pass;
576
577 p = password;
578 q = pass_buf[next_pass];
579 endq = q + sizeof pass_buf[next_pass];
580
581 while (*p && q < endq)
582 {
583 if (*p == '\"' || *p == '\\' || *p == '\'')
584 *q++ = '\\';
585 *q++ = *p++;
586 }
587
588 if (q >= endq)
589 return NULL;
590
591 *q = '\0';
592 return pass_buf[next_pass];
593}
594
595/* siefca@pld.org.pl */
596static char *parse_select_clause (const char *clause, const char *username,
597 const char *defdomain,
598 const char *service)
599{
600static struct var_data vd[]={
601 {"local_part", NULL, sizeof("local_part"), 0},
602 {"domain", NULL, sizeof("domain"), 0},
603 {"service", NULL, sizeof("service"), 0},
604 {NULL, NULL, 0, 0}};
605
606 if (clause == NULL || *clause == '\0' ||
607 !username || *username == '\0')
608 return NULL;
609
610 vd[0].value = get_localpart (username);
611 vd[1].value = get_domain (username, defdomain);
612 if (!vd[0].value || !vd[1].value)
613 return NULL;
614 vd[2].value = service;
615
616 return (parse_string (clause, vd));
617}
618
619/* siefca@pld.org.pl */
620static char *parse_chpass_clause (const char *clause, const char *username,
621 const char *defdomain, const char *newpass,
622 const char *newpass_crypt)
623{
624static struct var_data vd[]={
625 {"local_part", NULL, sizeof("local_part"), 0},
626 {"domain", NULL, sizeof("domain"), 0},
627 {"newpass", NULL, sizeof("newpass"), 0},
628 {"newpass_crypt", NULL, sizeof("newpass_crypt"), 0},
629 {NULL, NULL, 0, 0}};
630
631 if (clause == NULL || *clause == '\0' ||
632 !username || *username == '\0' ||
633 !newpass || *newpass == '\0' ||
634 !newpass_crypt || *newpass_crypt == '\0') return NULL;
635
636 vd[0].value = get_localpart (username);
637 vd[1].value = get_domain (username, defdomain);
638 vd[2].value = validateMyPassword (newpass);
639 vd[3].value = validateMyPassword (newpass_crypt);
640
641 if (!vd[0].value || !vd[1].value ||
642 !vd[2].value || !vd[3].value) return NULL;
643
644 return (parse_string (clause, vd));
645}
646
647struct authmysqluserinfo *auth_mysql_getuserinfo(const char *username,
648 const char *service)
649{
650const char *defdomain =NULL;
651char *querybuf, *p;
652MYSQL_ROW row;
653MYSQL_RES *result;
654int num_fields;
655char *endp;
656
657const char *select_clause; /* siefca@pld.org.pl */
658
659static const char query[]=
660 "SELECT %s, %s, %s, %s, %s, %s, %s, %s, %s, %s FROM %s WHERE %s = \"";
661
662 if (do_connect()) return (0);
663
664 initui();
665
666 select_clause=read_env("MYSQL_SELECT_CLAUSE");
667 defdomain=read_env("DEFAULT_DOMAIN");
668 if (!defdomain) defdomain="";
669
670 if (!select_clause) /* siefca@pld.org.pl */
671 {
672 const char *user_table,
673 *crypt_field,
674 *clear_field,
675 *name_field,
676 *uid_field,
677 *gid_field,
678 *login_field,
679 *home_field,
680 *maildir_field,
681 *quota_field,
682 *options_field,
683 *where_clause;
684
685 user_table=read_env("MYSQL_USER_TABLE");
686
687 if (!user_table)
688 {
689 err("authmysql: MYSQL_USER_TABLE not set in "
690 AUTHMYSQLRC ".");
691 return (0);
692 }
693
694 crypt_field=read_env("MYSQL_CRYPT_PWFIELD");
695 clear_field=read_env("MYSQL_CLEAR_PWFIELD");
696 name_field=read_env("MYSQL_NAME_FIELD");
697
698 if (!crypt_field && !clear_field)
699 {
700 err("authmysql: MYSQL_CRYPT_PWFIELD and "
701 "MYSQL_CLEAR_PWFIELD not set in " AUTHMYSQLRC ".");
702 return (0);
703 }
704 if (!crypt_field) crypt_field="\"\"";
705 if (!clear_field) clear_field="\"\"";
706 if (!name_field) name_field="\"\"";
707
708 uid_field = read_env("MYSQL_UID_FIELD");
709 if (!uid_field) uid_field = "uid";
710
711 gid_field = read_env("MYSQL_GID_FIELD");
712 if (!gid_field) gid_field = "gid";
713
714 login_field = read_env("MYSQL_LOGIN_FIELD");
715 if (!login_field) login_field = "id";
716
717 home_field = read_env("MYSQL_HOME_FIELD");
718 if (!home_field) home_field = "home";
719
720 maildir_field=read_env(service && strcmp(service, "courier")==0
721 ? "MYSQL_DEFAULTDELIVERY"
722 : "MYSQL_MAILDIR_FIELD");
723 if (!maildir_field) maildir_field="\"\"";
724
725 quota_field=read_env("MYSQL_QUOTA_FIELD");
726 if (!quota_field) quota_field="\"\"";
727
728 options_field=read_env("MYSQL_AUXOPTIONS_FIELD");
729 if (!options_field) options_field="\"\"";
730
731 where_clause=read_env("MYSQL_WHERE_CLAUSE");
732 if (!where_clause) where_clause = "";
733
734 querybuf=malloc(sizeof(query) + 100
735 + 2 * strlen(login_field)
736 + strlen(crypt_field)
737 + strlen(clear_field)
738 + strlen(uid_field) + strlen(gid_field)
739 + strlen(home_field)
740 + strlen(maildir_field)
741 + strlen(quota_field)
742 + strlen(name_field)
743 + strlen(options_field)
744 + strlen(user_table)
745 + strlen(username)
746 + strlen(defdomain)
747 + strlen(where_clause));
748
749 if (!querybuf)
750 {
751 perror("malloc");
752 return (0);
753 }
754
755 sprintf(querybuf, query, login_field, crypt_field, clear_field,
756 uid_field, gid_field, home_field, maildir_field, quota_field,
757 name_field, options_field, user_table, login_field);
758
759 p=querybuf+strlen(querybuf);
760
761 append_username(p, username, defdomain);
762 strcat(p, "\"");
763
764 if (strcmp(where_clause, "")) {
765 strcat(p, " AND (");
766 strcat(p, where_clause);
767 strcat(p, ")");
768 }
769 }
770 else
771 {
772 /* siefca@pld.org.pl */
773 querybuf=parse_select_clause (select_clause, username,
774 defdomain, service);
775 if (!querybuf)
776 {
777 DPRINTF("parse_select_clause failed (DEFAULT_DOMAIN not set?)");
778 return 0;
779 }
780 }
781
64ff59ba 782/* Anton Dobkin <anton@viansib.ru>, VIAN, Ltd. */
783#if MYSQL_VERSION_ID >= 41000
784 const char *character_set=read_env("MYSQL_CHARACTER_SET");
785
786 if(character_set){
787
788 char *character_set_buf;
789
790 character_set_buf=malloc(strlen(character_set)+11);
791
792 if (!character_set_buf)
793 {
794 perror("malloc");
795 return (0);
796 }
797
798 strcpy(character_set_buf, "SET NAMES ");
799 strcat(character_set_buf, character_set);
800
801 DPRINTF("Install of a character set for MySQL. SQL query: SET NAMES %s", character_set);
802
803 if(mysql_query (mysql, character_set_buf))
804 {
805 err("Install of a character set for MySQL is failed: %s MYSQL_CHARACTER_SET: may be invalid character set", mysql_error(mysql));
806 auth_mysql_cleanup();
807
808 if (do_connect())
809 {
810 free(character_set_buf);
811 return (0);
812 }
813 }
814
815 free(character_set_buf);
816 }
817#endif
818
d9898ee8 819 DPRINTF("SQL query: %s", querybuf);
820 if (mysql_query (mysql, querybuf))
821 {
822 /* <o.blasnik@nextra.de> */
823
824 DPRINTF("mysql_query failed, reconnecting: %s", mysql_error(mysql));
825 auth_mysql_cleanup();
826
827 if (do_connect())
828 {
829 free(querybuf);
830 return (0);
831 }
832
833 if (mysql_query (mysql, querybuf))
834 {
835 DPRINTF("mysql_query failed second time, giving up: %s", mysql_error(mysql));
836 free(querybuf);
837 auth_mysql_cleanup();
838 /* Server went down, that's OK,
839 ** try again next time.
840 */
841 return (0);
842 }
843 }
844 free(querybuf);
845
846 result = mysql_store_result (mysql);
847 if (result)
848 {
849 if (mysql_num_rows(result))
850 {
851 row = mysql_fetch_row (result);
852 num_fields = mysql_num_fields (result);
853
854 if (num_fields < 6)
855 {
856 DPRINTF("incomplete row, only %d fields returned",
857 num_fields);
858 mysql_free_result(result);
859 return(0);
860 }
861
862 if (row[0] && row[0][0])
863 ui.username=strdup(row[0]);
864 if (row[1] && row[1][0])
865 ui.cryptpw=strdup(row[1]);
866 if (row[2] && row[2][0])
867 ui.clearpw=strdup(row[2]);
868 /* perhaps authmysql needs a glob_uid/glob_gid feature
869 like authldap? */
870 if (!row[3] || !row[3][0] ||
871 (ui.uid=strtol(row[3], &endp, 10), endp[0] != '\0'))
872 {
873 DPRINTF("invalid value for uid: '%s'",
874 row[3] ? row[3] : "<null>");
875 mysql_free_result(result);
876 return 0;
877 }
878 if (!row[4] || !row[4][0] ||
879 (ui.gid=strtol(row[4], &endp, 10), endp[0] != '\0'))
880 {
881 DPRINTF("invalid value for gid: '%s'",
882 row[4] ? row[4] : "<null>");
883 mysql_free_result(result);
884 return 0;
885 }
886 if (row[5] && row[5][0])
887 ui.home=strdup(row[5]);
888 else
889 {
890 DPRINTF("required value for 'home' (column 6) is missing");
891 mysql_free_result(result);
892 return(0);
893 }
894 if (num_fields > 6 && row[6] && row[6][0])
895 ui.maildir=strdup(row[6]);
896 if (num_fields > 7 && row[7] && row[7][0])
897 ui.quota=strdup(row[7]);
898 if (num_fields > 8 && row[8] && row[8][0])
899 ui.fullname=strdup(row[8]);
900 if (num_fields > 9 && row[9] && row[9][0])
901 ui.options=strdup(row[9]);
902 }
903 else
904 {
905 DPRINTF("zero rows returned");
906 mysql_free_result(result);
907 return (&ui); /* User not found */
908 }
909 }
910 else
911 {
912 DPRINTF("mysql_store_result failed");
913 return (0);
914 }
915 mysql_free_result(result);
916 return (&ui);
917}
918
919int auth_mysql_setpass(const char *user, const char *pass,
920 const char *oldpass)
921{
922 char *newpass_crypt;
923 const char *p;
924 int l;
925 char *sql_buf;
926 const char *comma;
927 int rc=0;
928
929 const char *clear_field =NULL,
930 *crypt_field =NULL,
931 *defdomain =NULL,
932 *where_clause =NULL,
933 *user_table =NULL,
934 *login_field =NULL,
935 *chpass_clause =NULL; /* siefca@pld.org.pl */
936
937 if (!mysql)
938 return (-1);
939
940
941 if (!(newpass_crypt=authcryptpasswd(pass, oldpass)))
942 return (-1);
943
944 for (l=0, p=pass; *p; p++)
945 {
946 if ((int)(unsigned char)*p < ' ')
947 {
948 free(newpass_crypt);
949 return (-1);
950 }
951 if (*p == '"' || *p == '\\')
952 ++l;
953 ++l;
954 }
955
956 /* siefca@pld.org.pl */
957 chpass_clause=read_env("MYSQL_CHPASS_CLAUSE");
958 defdomain=read_env("DEFAULT_DOMAIN");
959 user_table=read_env("MYSQL_USER_TABLE");
960 if (!chpass_clause)
961 {
962 login_field = read_env("MYSQL_LOGIN_FIELD");
963 if (!login_field) login_field = "id";
964 crypt_field=read_env("MYSQL_CRYPT_PWFIELD");
965 clear_field=read_env("MYSQL_CLEAR_PWFIELD");
966 where_clause=read_env("MYSQL_WHERE_CLAUSE");
967 sql_buf=malloc(strlen(crypt_field ? crypt_field:"")
968 + strlen(clear_field ? clear_field:"")
969 + strlen(defdomain ? defdomain:"")
970 + strlen(login_field) + l + strlen(newpass_crypt)
971 + strlen(user_table)
972 + strlen(where_clause ? where_clause:"")
973 + 200);
974 }
975 else
976 {
977 sql_buf=parse_chpass_clause(chpass_clause,
978 user,
979 defdomain,
980 pass,
981 newpass_crypt);
982 }
983
984
985 if (!sql_buf)
986 {
987 free(newpass_crypt);
988 return (-1);
989 }
990
991 if (!chpass_clause) /*siefca@pld.org.pl */
992 {
993 sprintf(sql_buf, "UPDATE %s SET", user_table);
994
995 comma="";
996
997 if (clear_field && *clear_field)
998 {
999 char *q;
1000
1001 strcat(strcat(strcat(sql_buf, " "), clear_field),
1002 "=\"");
1003
1004 q=sql_buf+strlen(sql_buf);
1005 while (*pass)
1006 {
1007 if (*pass == '"' || *pass == '\\')
1008 *q++= '\\';
1009 *q++ = *pass++;
1010 }
1011 strcpy(q, "\"");
1012 comma=", ";
1013 }
1014
1015 if (crypt_field && *crypt_field)
1016 {
1017 strcat(strcat(strcat(strcat(strcat(strcat(sql_buf, comma),
1018 " "),
1019 crypt_field),
1020 "=\""),
1021 newpass_crypt),
1022 "\"");
1023 }
1024 free(newpass_crypt);
1025
1026 strcat(strcat(strcat(sql_buf, " WHERE "),
1027 login_field),
1028 "=\"");
1029
1030 append_username(sql_buf+strlen(sql_buf), user, defdomain);
1031
1032 strcat(sql_buf, "\"");
1033
1034 if (where_clause && *where_clause)
1035 {
1036 strcat(sql_buf, " AND (");
1037 strcat(sql_buf, where_clause);
1038 strcat(sql_buf, ")");
1039 }
1040
1041 }
1042 else
1043 {
1044 free(newpass_crypt);
1045 }
1046
1047
1048 if (courier_authdebug_login_level >= 2)
1049 {
1050 DPRINTF("setpass SQL: %s", sql_buf);
1051 }
1052 if (mysql_query (mysql, sql_buf))
1053 {
1054 DPRINTF("setpass SQL failed");
1055 rc= -1;
1056 auth_mysql_cleanup();
1057 }
1058 free(sql_buf);
1059 return (rc);
1060}
1061
1062void auth_mysql_enumerate( void(*cb_func)(const char *name,
1063 uid_t uid,
1064 gid_t gid,
1065 const char *homedir,
1066 const char *maildir,
1067 const char *options,
1068 void *void_arg),
1069 void *void_arg)
1070{
1071 const char *defdomain, *select_clause;
1072 char *querybuf, *p;
1073 MYSQL_ROW row;
1074 MYSQL_RES *result;
1075
1076 static const char query[]=
1077 "SELECT %s, %s, %s, %s, %s, %s FROM %s WHERE 1=1";
1078
1079 if (do_connect()) return;
1080
1081 initui();
1082
1083 select_clause=read_env("MYSQL_ENUMERATE_CLAUSE");
1084 defdomain=read_env("DEFAULT_DOMAIN");
1085 if (!defdomain || !defdomain[0])
1086 defdomain="*"; /* otherwise parse_select_clause fails */
1087
1088 if (!select_clause)
1089 {
1090 const char *user_table,
1091 *uid_field,
1092 *gid_field,
1093 *login_field,
1094 *home_field,
1095 *maildir_field,
1096 *options_field,
1097 *where_clause;
1098
1099 user_table=read_env("MYSQL_USER_TABLE");
1100
1101 if (!user_table)
1102 {
1103 err("authmysql: MYSQL_USER_TABLE not set in "
1104 AUTHMYSQLRC ".");
1105 return;
1106 }
1107
1108 uid_field = read_env("MYSQL_UID_FIELD");
1109 if (!uid_field) uid_field = "uid";
1110
1111 gid_field = read_env("MYSQL_GID_FIELD");
1112 if (!gid_field) gid_field = "gid";
1113
1114 login_field = read_env("MYSQL_LOGIN_FIELD");
1115 if (!login_field) login_field = "id";
1116
1117 home_field = read_env("MYSQL_HOME_FIELD");
1118 if (!home_field) home_field = "home";
1119
1120 maildir_field=read_env("MYSQL_MAILDIR_FIELD");
1121 if (!maildir_field) maildir_field="\"\"";
1122
1123 options_field=read_env("MYSQL_AUXOPTIONS_FIELD");
1124 if (!options_field) options_field="\"\"";
1125
1126 where_clause=read_env("MYSQL_WHERE_CLAUSE");
1127 if (!where_clause) where_clause = "";
1128
1129 querybuf=malloc(sizeof(query) + 100
1130 + strlen(login_field)
1131 + strlen(uid_field) + strlen(gid_field)
1132 + strlen(home_field)
1133 + strlen(maildir_field)
1134 + strlen(options_field)
1135 + strlen(user_table)
1136 + strlen(where_clause));
1137
1138 if (!querybuf)
1139 {
1140 perror("malloc");
1141 return;
1142 }
1143
1144 sprintf(querybuf, query, login_field,
1145 uid_field, gid_field, home_field, maildir_field,
1146 options_field, user_table);
1147
1148 p=querybuf+strlen(querybuf);
1149
1150 if (strcmp(where_clause, "")) {
1151 strcat(p, " AND (");
1152 strcat(p, where_clause);
1153 strcat(p, ")");
1154 }
1155 }
1156 else
1157 {
1158 /* siefca@pld.org.pl */
1159 querybuf=parse_select_clause (select_clause, "*",
1160 defdomain, "enumerate");
1161 if (!querybuf)
1162 {
1163 DPRINTF("authmysql: parse_select_clause failed");
1164 return;
1165 }
1166 }
1167 DPRINTF("authmysql: enumerate query: %s", querybuf);
1168
1169 if (mysql_query (mysql, querybuf))
1170 {
1171 DPRINTF("mysql_query failed, reconnecting: %s", mysql_error(mysql));
1172 /* <o.blasnik@nextra.de> */
1173
1174 auth_mysql_cleanup();
1175
1176 if (do_connect())
1177 {
1178 free(querybuf);
1179 return;
1180 }
1181
1182 if (mysql_query (mysql, querybuf))
1183 {
1184 DPRINTF("mysql_query failed second time, giving up: %s", mysql_error(mysql));
1185 free(querybuf);
1186 auth_mysql_cleanup();
1187 return;
1188 }
1189 }
1190 free(querybuf);
1191
1192 result = mysql_use_result (mysql);
1193 if (result)
1194 {
1195 const char *username;
1196 uid_t uid;
1197 gid_t gid;
1198 const char *homedir;
1199 const char *maildir;
1200 const char *options;
1201
1202 while ((row = mysql_fetch_row (result)) != NULL)
1203 {
1204 if(!row[0] || !row[0][0]
1205 || !row[1] || !row[1][0]
1206 || !row[2] || !row[2][0]
1207 || !row[3] || !row[3][0])
1208 {
1209 continue;
1210 }
1211
1212 username=row[0];
1213 uid=atol(row[1]); /* FIXME use strtol to validate */
1214 gid=atol(row[2]);
1215 homedir=row[3];
1216 maildir=row[4];
1217 options=row[5];
1218
1219 if (maildir && !*maildir)
1220 maildir=NULL;
1221
1222 (*cb_func)(username, uid, gid, homedir,
1223 maildir, options, void_arg);
1224 }
1225 }
1226 /* NULL row could indicate end of result or an error */
1227 if (mysql_errno(mysql))
1228 {
1229 DPRINTF("mysql error during enumeration: %s", mysql_error(mysql));
1230 }
1231 else
1232 (*cb_func)(NULL, 0, 0, NULL, NULL, NULL, void_arg);
1233
1234 if (result) mysql_free_result(result);
1235}