Imported upstream version 0.59.3
[hcoop/debian/courier-authlib.git] / authmysqllib.c
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
30 static const char rcsid[]="$Id: authmysqllib.c,v 1.44 2007/02/10 02:51:06 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 int do_connect()
114 {
115 const char *server;
116 const char *userid;
117 const char *password;
118 const char *database;
119 const char *server_socket=0;
120 unsigned int server_port=0;
121 unsigned int server_opt=0;
122 const char *p;
123
124 const char *sslkey;
125 const char *sslcert;
126 const char *sslcacert;
127 const char *sslcapath;
128 const char *sslcipher;
129 unsigned 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
242 void auth_mysql_cleanup()
243 {
244 if (mysql)
245 {
246 mysql_close(mysql);
247 mysql=0;
248 }
249 }
250
251 static struct authmysqluserinfo ui={0, 0, 0, 0, 0, 0, 0, 0};
252
253 static 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
275 static 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 */
287 static struct var_data *get_variable (const char *begin, size_t len,
288 struct var_data *vdt)
289 {
290 struct 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 */
335 static 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 */
350 static int ParsePlugin_builder (const char *p, size_t length, void *vp)
351 {
352 char **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 */
369 static int parse_core (const char *source, struct var_data *vdt,
370 parsefunc outfn, void *result)
371 {
372 size_t v_size = 0,
373 t_size = 0;
374 const char *p, *q, *e,
375 *v_begin, *v_end,
376 *t_begin, *t_end;
377 struct 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 */
447 static char *parse_string (const char *source, struct var_data *vdt)
448 {
449 struct var_data *vdp = NULL;
450 char *output_buf = NULL,
451 *pass_buf = NULL;
452 size_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 */
492 static const char *get_localpart (const char *username)
493 {
494 size_t lbuf = 0;
495 const char *l_end, *p;
496 char *q;
497 static 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 */
530 static const char *get_domain (const char *username, const char *defdomain)
531 {
532 static char domain_buf[260];
533 const char *p;
534 char *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
565 static const char *validateMyPassword (const char *password)
566 {
567 static char pass_buf[2][540]; /* Use two buffers, see parse_chpass_clause */
568 static int next_pass=0;
569 const char *p;
570 char *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 */
596 static char *parse_select_clause (const char *clause, const char *username,
597 const char *defdomain,
598 const char *service)
599 {
600 static 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 */
620 static char *parse_chpass_clause (const char *clause, const char *username,
621 const char *defdomain, const char *newpass,
622 const char *newpass_crypt)
623 {
624 static 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
647 struct authmysqluserinfo *auth_mysql_getuserinfo(const char *username,
648 const char *service)
649 {
650 const char *defdomain =NULL;
651 char *querybuf, *p;
652 MYSQL_ROW row;
653 MYSQL_RES *result;
654 int num_fields;
655 char *endp;
656
657 const char *select_clause; /* siefca@pld.org.pl */
658
659 static 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
782 DPRINTF("SQL query: %s", querybuf);
783 if (mysql_query (mysql, querybuf))
784 {
785 /* <o.blasnik@nextra.de> */
786
787 DPRINTF("mysql_query failed, reconnecting: %s", mysql_error(mysql));
788 auth_mysql_cleanup();
789
790 if (do_connect())
791 {
792 free(querybuf);
793 return (0);
794 }
795
796 if (mysql_query (mysql, querybuf))
797 {
798 DPRINTF("mysql_query failed second time, giving up: %s", mysql_error(mysql));
799 free(querybuf);
800 auth_mysql_cleanup();
801 /* Server went down, that's OK,
802 ** try again next time.
803 */
804 return (0);
805 }
806 }
807 free(querybuf);
808
809 result = mysql_store_result (mysql);
810 if (result)
811 {
812 if (mysql_num_rows(result))
813 {
814 row = mysql_fetch_row (result);
815 num_fields = mysql_num_fields (result);
816
817 if (num_fields < 6)
818 {
819 DPRINTF("incomplete row, only %d fields returned",
820 num_fields);
821 mysql_free_result(result);
822 return(0);
823 }
824
825 if (row[0] && row[0][0])
826 ui.username=strdup(row[0]);
827 if (row[1] && row[1][0])
828 ui.cryptpw=strdup(row[1]);
829 if (row[2] && row[2][0])
830 ui.clearpw=strdup(row[2]);
831 /* perhaps authmysql needs a glob_uid/glob_gid feature
832 like authldap? */
833 if (!row[3] || !row[3][0] ||
834 (ui.uid=strtol(row[3], &endp, 10), endp[0] != '\0'))
835 {
836 DPRINTF("invalid value for uid: '%s'",
837 row[3] ? row[3] : "<null>");
838 mysql_free_result(result);
839 return 0;
840 }
841 if (!row[4] || !row[4][0] ||
842 (ui.gid=strtol(row[4], &endp, 10), endp[0] != '\0'))
843 {
844 DPRINTF("invalid value for gid: '%s'",
845 row[4] ? row[4] : "<null>");
846 mysql_free_result(result);
847 return 0;
848 }
849 if (row[5] && row[5][0])
850 ui.home=strdup(row[5]);
851 else
852 {
853 DPRINTF("required value for 'home' (column 6) is missing");
854 mysql_free_result(result);
855 return(0);
856 }
857 if (num_fields > 6 && row[6] && row[6][0])
858 ui.maildir=strdup(row[6]);
859 if (num_fields > 7 && row[7] && row[7][0])
860 ui.quota=strdup(row[7]);
861 if (num_fields > 8 && row[8] && row[8][0])
862 ui.fullname=strdup(row[8]);
863 if (num_fields > 9 && row[9] && row[9][0])
864 ui.options=strdup(row[9]);
865 }
866 else
867 {
868 DPRINTF("zero rows returned");
869 mysql_free_result(result);
870 return (&ui); /* User not found */
871 }
872 }
873 else
874 {
875 DPRINTF("mysql_store_result failed");
876 return (0);
877 }
878 mysql_free_result(result);
879 return (&ui);
880 }
881
882 int auth_mysql_setpass(const char *user, const char *pass,
883 const char *oldpass)
884 {
885 char *newpass_crypt;
886 const char *p;
887 int l;
888 char *sql_buf;
889 const char *comma;
890 int rc=0;
891
892 const char *clear_field =NULL,
893 *crypt_field =NULL,
894 *defdomain =NULL,
895 *where_clause =NULL,
896 *user_table =NULL,
897 *login_field =NULL,
898 *chpass_clause =NULL; /* siefca@pld.org.pl */
899
900 if (!mysql)
901 return (-1);
902
903
904 if (!(newpass_crypt=authcryptpasswd(pass, oldpass)))
905 return (-1);
906
907 for (l=0, p=pass; *p; p++)
908 {
909 if ((int)(unsigned char)*p < ' ')
910 {
911 free(newpass_crypt);
912 return (-1);
913 }
914 if (*p == '"' || *p == '\\')
915 ++l;
916 ++l;
917 }
918
919 /* siefca@pld.org.pl */
920 chpass_clause=read_env("MYSQL_CHPASS_CLAUSE");
921 defdomain=read_env("DEFAULT_DOMAIN");
922 user_table=read_env("MYSQL_USER_TABLE");
923 if (!chpass_clause)
924 {
925 login_field = read_env("MYSQL_LOGIN_FIELD");
926 if (!login_field) login_field = "id";
927 crypt_field=read_env("MYSQL_CRYPT_PWFIELD");
928 clear_field=read_env("MYSQL_CLEAR_PWFIELD");
929 where_clause=read_env("MYSQL_WHERE_CLAUSE");
930 sql_buf=malloc(strlen(crypt_field ? crypt_field:"")
931 + strlen(clear_field ? clear_field:"")
932 + strlen(defdomain ? defdomain:"")
933 + strlen(login_field) + l + strlen(newpass_crypt)
934 + strlen(user_table)
935 + strlen(where_clause ? where_clause:"")
936 + 200);
937 }
938 else
939 {
940 sql_buf=parse_chpass_clause(chpass_clause,
941 user,
942 defdomain,
943 pass,
944 newpass_crypt);
945 }
946
947
948 if (!sql_buf)
949 {
950 free(newpass_crypt);
951 return (-1);
952 }
953
954 if (!chpass_clause) /*siefca@pld.org.pl */
955 {
956 sprintf(sql_buf, "UPDATE %s SET", user_table);
957
958 comma="";
959
960 if (clear_field && *clear_field)
961 {
962 char *q;
963
964 strcat(strcat(strcat(sql_buf, " "), clear_field),
965 "=\"");
966
967 q=sql_buf+strlen(sql_buf);
968 while (*pass)
969 {
970 if (*pass == '"' || *pass == '\\')
971 *q++= '\\';
972 *q++ = *pass++;
973 }
974 strcpy(q, "\"");
975 comma=", ";
976 }
977
978 if (crypt_field && *crypt_field)
979 {
980 strcat(strcat(strcat(strcat(strcat(strcat(sql_buf, comma),
981 " "),
982 crypt_field),
983 "=\""),
984 newpass_crypt),
985 "\"");
986 }
987 free(newpass_crypt);
988
989 strcat(strcat(strcat(sql_buf, " WHERE "),
990 login_field),
991 "=\"");
992
993 append_username(sql_buf+strlen(sql_buf), user, defdomain);
994
995 strcat(sql_buf, "\"");
996
997 if (where_clause && *where_clause)
998 {
999 strcat(sql_buf, " AND (");
1000 strcat(sql_buf, where_clause);
1001 strcat(sql_buf, ")");
1002 }
1003
1004 }
1005 else
1006 {
1007 free(newpass_crypt);
1008 }
1009
1010
1011 if (courier_authdebug_login_level >= 2)
1012 {
1013 DPRINTF("setpass SQL: %s", sql_buf);
1014 }
1015 if (mysql_query (mysql, sql_buf))
1016 {
1017 DPRINTF("setpass SQL failed");
1018 rc= -1;
1019 auth_mysql_cleanup();
1020 }
1021 free(sql_buf);
1022 return (rc);
1023 }
1024
1025 void auth_mysql_enumerate( void(*cb_func)(const char *name,
1026 uid_t uid,
1027 gid_t gid,
1028 const char *homedir,
1029 const char *maildir,
1030 const char *options,
1031 void *void_arg),
1032 void *void_arg)
1033 {
1034 const char *defdomain, *select_clause;
1035 char *querybuf, *p;
1036 MYSQL_ROW row;
1037 MYSQL_RES *result;
1038
1039 static const char query[]=
1040 "SELECT %s, %s, %s, %s, %s, %s FROM %s WHERE 1=1";
1041
1042 if (do_connect()) return;
1043
1044 initui();
1045
1046 select_clause=read_env("MYSQL_ENUMERATE_CLAUSE");
1047 defdomain=read_env("DEFAULT_DOMAIN");
1048 if (!defdomain || !defdomain[0])
1049 defdomain="*"; /* otherwise parse_select_clause fails */
1050
1051 if (!select_clause)
1052 {
1053 const char *user_table,
1054 *uid_field,
1055 *gid_field,
1056 *login_field,
1057 *home_field,
1058 *maildir_field,
1059 *options_field,
1060 *where_clause;
1061
1062 user_table=read_env("MYSQL_USER_TABLE");
1063
1064 if (!user_table)
1065 {
1066 err("authmysql: MYSQL_USER_TABLE not set in "
1067 AUTHMYSQLRC ".");
1068 return;
1069 }
1070
1071 uid_field = read_env("MYSQL_UID_FIELD");
1072 if (!uid_field) uid_field = "uid";
1073
1074 gid_field = read_env("MYSQL_GID_FIELD");
1075 if (!gid_field) gid_field = "gid";
1076
1077 login_field = read_env("MYSQL_LOGIN_FIELD");
1078 if (!login_field) login_field = "id";
1079
1080 home_field = read_env("MYSQL_HOME_FIELD");
1081 if (!home_field) home_field = "home";
1082
1083 maildir_field=read_env("MYSQL_MAILDIR_FIELD");
1084 if (!maildir_field) maildir_field="\"\"";
1085
1086 options_field=read_env("MYSQL_AUXOPTIONS_FIELD");
1087 if (!options_field) options_field="\"\"";
1088
1089 where_clause=read_env("MYSQL_WHERE_CLAUSE");
1090 if (!where_clause) where_clause = "";
1091
1092 querybuf=malloc(sizeof(query) + 100
1093 + strlen(login_field)
1094 + strlen(uid_field) + strlen(gid_field)
1095 + strlen(home_field)
1096 + strlen(maildir_field)
1097 + strlen(options_field)
1098 + strlen(user_table)
1099 + strlen(where_clause));
1100
1101 if (!querybuf)
1102 {
1103 perror("malloc");
1104 return;
1105 }
1106
1107 sprintf(querybuf, query, login_field,
1108 uid_field, gid_field, home_field, maildir_field,
1109 options_field, user_table);
1110
1111 p=querybuf+strlen(querybuf);
1112
1113 if (strcmp(where_clause, "")) {
1114 strcat(p, " AND (");
1115 strcat(p, where_clause);
1116 strcat(p, ")");
1117 }
1118 }
1119 else
1120 {
1121 /* siefca@pld.org.pl */
1122 querybuf=parse_select_clause (select_clause, "*",
1123 defdomain, "enumerate");
1124 if (!querybuf)
1125 {
1126 DPRINTF("authmysql: parse_select_clause failed");
1127 return;
1128 }
1129 }
1130 DPRINTF("authmysql: enumerate query: %s", querybuf);
1131
1132 if (mysql_query (mysql, querybuf))
1133 {
1134 DPRINTF("mysql_query failed, reconnecting: %s", mysql_error(mysql));
1135 /* <o.blasnik@nextra.de> */
1136
1137 auth_mysql_cleanup();
1138
1139 if (do_connect())
1140 {
1141 free(querybuf);
1142 return;
1143 }
1144
1145 if (mysql_query (mysql, querybuf))
1146 {
1147 DPRINTF("mysql_query failed second time, giving up: %s", mysql_error(mysql));
1148 free(querybuf);
1149 auth_mysql_cleanup();
1150 return;
1151 }
1152 }
1153 free(querybuf);
1154
1155 result = mysql_use_result (mysql);
1156 if (result)
1157 {
1158 const char *username;
1159 uid_t uid;
1160 gid_t gid;
1161 const char *homedir;
1162 const char *maildir;
1163 const char *options;
1164
1165 while ((row = mysql_fetch_row (result)) != NULL)
1166 {
1167 if(!row[0] || !row[0][0]
1168 || !row[1] || !row[1][0]
1169 || !row[2] || !row[2][0]
1170 || !row[3] || !row[3][0])
1171 {
1172 continue;
1173 }
1174
1175 username=row[0];
1176 uid=atol(row[1]); /* FIXME use strtol to validate */
1177 gid=atol(row[2]);
1178 homedir=row[3];
1179 maildir=row[4];
1180 options=row[5];
1181
1182 if (maildir && !*maildir)
1183 maildir=NULL;
1184
1185 (*cb_func)(username, uid, gid, homedir,
1186 maildir, options, void_arg);
1187 }
1188 }
1189 /* NULL row could indicate end of result or an error */
1190 if (mysql_errno(mysql))
1191 {
1192 DPRINTF("mysql error during enumeration: %s", mysql_error(mysql));
1193 }
1194 else
1195 (*cb_func)(NULL, 0, 0, NULL, NULL, NULL, void_arg);
1196
1197 if (result) mysql_free_result(result);
1198 }