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