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