hcoop release
[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
d9898ee8 31
32/* tom@minnesota.com */
33struct var_data {
34 const char *name;
35 const char *value;
36 const size_t size;
d9898ee8 37 } ;
38
39/* tom@minnesota.com */
40typedef int (*parsefunc)(const char *, size_t, void *);
41
42static const char *read_env(const char *env)
43{
44static char *pgsqlauth=0;
45static size_t pgsqlauth_size=0;
46size_t i;
47char *p=0;
48int l=strlen(env);
49
50 if (!pgsqlauth)
51 {
52 FILE *f=fopen(AUTHPGSQLRC, "r");
53 struct stat buf;
54
55 if (!f) return (0);
56 if (fstat(fileno(f), &buf) ||
57 (pgsqlauth=malloc(buf.st_size+2)) == 0)
58 {
59 fclose(f);
60 return (0);
61 }
62 if (fread(pgsqlauth, buf.st_size, 1, f) != 1)
63 {
64 free(pgsqlauth);
65 pgsqlauth=0;
66 fclose(f);
67 return (0);
68 }
69 pgsqlauth[pgsqlauth_size=buf.st_size]=0;
70
71 for (i=0; i<pgsqlauth_size; i++)
72 if (pgsqlauth[i] == '\n')
73 { /* tom@minnesota.com */
74 if (!i || pgsqlauth[i-1] != '\\')
75 {
76 pgsqlauth[i]='\0';
77 }
78 else
79 {
80 pgsqlauth[i]=pgsqlauth[i-1]= ' ';
81 }
82 }
83 fclose(f);
84 }
85
86 for (i=0; i<pgsqlauth_size; )
87 {
88 p=pgsqlauth+i;
89 if (memcmp(p, env, l) == 0 &&
90 isspace((int)(unsigned char)p[l]))
91 {
92 p += l;
93 while (*p && *p != '\n' &&
94 isspace((int)(unsigned char)*p))
95 ++p;
96 break;
97 }
98
99 while (i < pgsqlauth_size)
100 if (pgsqlauth[i++] == 0) break;
101 }
102
103 if (i < pgsqlauth_size)
104 return (p);
105 return (0);
106}
107
108static PGresult *pgresult=0;
109
110static PGconn *pgconn=0;
111
8d138742
CE
112/*
113* session variables can be set once for the whole session
114*/
115
116static void set_session_options(void)
117{
118 const char *character_set=read_env("PGSQL_CHARACTER_SET"), *check;
119
120 if (character_set)
121 {
122 PQsetClientEncoding(pgconn, character_set);
123 check = pg_encoding_to_char(PQclientEncoding(pgconn));
124 if (strcmp(character_set, check) != 0)
125 {
126 err("Cannot set Postgresql character set \"%s\", working with \"%s\"\n",
127 character_set, check);
128 }
129 else
130 {
131 DPRINTF("Install of a character set for Postgresql: %s", character_set);
132 }
133 }
134}
135
136
137
d9898ee8 138/*
139static FILE *DEBUG=0;
140*/
141
142static int do_connect()
143{
144const char *server;
145const char *userid;
146const char *password;
147const char *database;
148const char *server_port=0;
149const char *server_opt=0;
150/*
151 if (!DEBUG) {
152 DEBUG=fopen("/tmp/courier.debug","a");
153 }
154 fprintf(DEBUG,"Apro il DB!\n");
155 fflush(DEBUG);
156*/
157
158/*
159** Periodically detect dead connections.
160*/
161 if (pgconn)
162 {
163 static time_t last_time=0;
164 time_t t_check;
165
166 time(&t_check);
167
168 if (t_check < last_time)
169 last_time=t_check; /* System clock changed */
170
171 if (t_check < last_time + 60)
172 return (0);
173
174 last_time=t_check;
175
176 if (PQstatus(pgconn) == CONNECTION_OK) return (0);
177
178 DPRINTF("authpgsqllib: PQstatus failed, connection lost");
179 PQfinish(pgconn);
180 pgconn=0;
181 }
182
183 server=read_env("PGSQL_HOST");
184 server_port=read_env("PGSQL_PORT");
185 userid=read_env("PGSQL_USERNAME");
186 password=read_env("PGSQL_PASSWORD");
187 database=read_env("PGSQL_DATABASE");
188 server_opt=read_env("PGSQL_OPT");
189
190/*
191 fprintf(DEBUG,"Letti i parametri!\n");
192 fflush(DEBUG);
193*/
194
195 if (!userid)
196 {
197 err("authpgsql: PGSQL_USERNAME not set in "
198 AUTHPGSQLRC ".");
199 return (-1);
200 }
201
202 if (!database)
203 {
204 err("authpgsql: PGSQL_DATABASE not set in "
205 AUTHPGSQLRC ".");
206 return (-1);
207 }
208
209/*
210 fprintf(DEBUG,"Connecting to db:%s:%s\%s user=%s pass=%s\n",server,server_port,database,userid,password);
211 fflush(DEBUG);
212*/
213 pgconn = PQsetdbLogin(server, server_port, server_opt, NULL , database,userid,password);
214
215 if (PQstatus(pgconn) == CONNECTION_BAD)
216 {
217 err("Connection to server '%s' userid '%s' database '%s' failed.",
218 server ? server : "<null>",
219 userid ? userid : "<null>",
220 database);
221 err("%s", PQerrorMessage(pgconn));
b0322a85 222 PQfinish(pgconn);
d9898ee8 223 pgconn=0;
224 return -1;
225 }
226/*
227 fprintf(DEBUG,"Connected!\n");
228 fflush(DEBUG);
229*/
230
8d138742 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
8d138742
CE
247static char *get_username_escaped(const char *username,
248 const char *defdomain)
d9898ee8 249{
8d138742
CE
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,
8d138742 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 {
8d138742
CE
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;
8d138742
CE
417
418 enc=malloc(strlen(v_ptr->value)*2+1);
419
420 if (!enc)
d9898ee8 421 return -1;
8d138742
CE
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
8d138742 483static char *get_localpart (const char *username)
d9898ee8 484{
8d138742
CE
485 char *p=strdup(username);
486 char *q;
d9898ee8 487
8d138742
CE
488 if (!p)
489 return 0;
d9898ee8 490
8d138742 491 q=strchr(p, '@');
d9898ee8 492
8d138742
CE
493 if (q)
494 *q=0;
d9898ee8 495
8d138742 496 return p;
d9898ee8 497}
498
8d138742 499static const char *get_domain (const char *username, const char *defdomain)
d9898ee8 500{
8d138742 501 const char *p=strchr(username, '@');
d9898ee8 502
8d138742
CE
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{
8d138742
CE
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
8d138742
CE
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);
8d138742
CE
531
532 if (!vd[1].value)
533 {
534 free(localpart);
d9898ee8 535 return NULL;
8d138742 536 }
d9898ee8 537 vd[2].value = service;
538
8d138742
CE
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{
8d138742
CE
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
8d138742
CE
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);
8d138742
CE
569 vd[2].value = newpass;
570 vd[3].value = newpass_crypt;
d9898ee8 571
8d138742
CE
572 if (!vd[1].value || !vd[2].value || !vd[3].value)
573 {
574 free(localpart);
575 return NULL;
576 }
d9898ee8 577
8d138742
CE
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{
8d138742
CE
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 if (do_connect()) return (0);
623
624 initui();
625
626/*
627 fprintf(DEBUG,"1Leggo parametri\n");
628 fflush(DEBUG);
629*/
630 select_clause=read_env("PGSQL_SELECT_CLAUSE");
631 defdomain=read_env("DEFAULT_DOMAIN");
632 if (!defdomain) defdomain="";
633
634 if (!select_clause) /* tom@minnesota.com */
635 {
636 const char *user_table,
637 *crypt_field,
638 *clear_field,
639 *name_field,
640 *uid_field,
641 *gid_field,
642 *login_field,
643 *home_field,
644 *maildir_field,
645 *quota_field,
646 *options_field,
647 *where_clause;
648
8d138742
CE
649 const char *where_pfix, *where_sfix;
650 char *username_escaped;
651
d9898ee8 652 user_table=read_env("PGSQL_USER_TABLE");
653
654 if (!user_table)
655 {
656 err("authpgsql: PGSQL_USER_TABLE not set in "
657 AUTHPGSQLRC ".");
658 return (0);
659 }
660
661 crypt_field=read_env("PGSQL_CRYPT_PWFIELD");
662 clear_field=read_env("PGSQL_CLEAR_PWFIELD");
663 name_field=read_env("PGSQL_NAME_FIELD");
664
665 if (!crypt_field && !clear_field)
666 {
667 err("authpgsql: PGSQL_CRYPT_PWFIELD and "
668 "PGSQL_CLEAR_PWFIELD not set in " AUTHPGSQLRC ".");
669 return (0);
670 }
671 if (!crypt_field) crypt_field="''";
672 if (!clear_field) clear_field="''";
673 if (!name_field) name_field="''";
674
675 uid_field = read_env("PGSQL_UID_FIELD");
676 if (!uid_field) uid_field = "uid";
677
678 gid_field = read_env("PGSQL_GID_FIELD");
679 if (!gid_field) gid_field = "gid";
680
681 login_field = read_env("PGSQL_LOGIN_FIELD");
682 if (!login_field) login_field = "id";
683
684 home_field = read_env("PGSQL_HOME_FIELD");
685 if (!home_field) home_field = "home";
686
687 maildir_field=read_env(service && strcmp(service, "courier")==0
688 ? "PGSQL_DEFAULTDELIVERY"
689 : "PGSQL_MAILDIR_FIELD");
690 if (!maildir_field) maildir_field="''";
691
692 quota_field=read_env("PGSQL_QUOTA_FIELD");
693 if (!quota_field) quota_field="''";
694
695 options_field=read_env("PGSQL_AUXOPTIONS_FIELD");
696 if (!options_field) options_field="''";
697
698 where_clause=read_env("PGSQL_WHERE_CLAUSE");
699 if (!where_clause) where_clause = "";
700
8d138742 701 where_pfix=where_sfix="";
d9898ee8 702
8d138742 703 if (strcmp(where_clause, ""))
d9898ee8 704 {
8d138742
CE
705 where_pfix=" AND (";
706 where_sfix=")";
d9898ee8 707 }
708
8d138742 709 username_escaped=get_username_escaped(username, defdomain);
d9898ee8 710
8d138742
CE
711 if (!username_escaped)
712 return 0;
713
714 query_size=snprintf(dummy_buf, 1, SELECT_QUERY);
715
716 querybuf=malloc(query_size+1);
717
718 if (!querybuf)
719 {
720 free(username_escaped);
721 perror("malloc");
722 return 0;
d9898ee8 723 }
8d138742
CE
724
725 snprintf(querybuf, query_size+1, SELECT_QUERY);
726 free(username_escaped);
d9898ee8 727 }
728 else
729 {
730 /* tom@minnesota.com */
731 querybuf=parse_select_clause (select_clause, username,
732 defdomain, service);
733 if (!querybuf)
734 {
735 DPRINTF("authpgsql: parse_select_clause failed (DEFAULT_DOMAIN not defined?)");
736 return 0;
737 }
738 }
739
740 DPRINTF("SQL query: %s", querybuf);
741 pgresult = PQexec(pgconn, querybuf);
742 if (!pgresult || PQresultStatus(pgresult) != PGRES_TUPLES_OK)
743 {
744 DPRINTF("PQexec failed, reconnecting: %s", PQerrorMessage(pgconn));
745 if (pgresult) PQclear(pgresult);
746
747 /* <o.blasnik@nextra.de> */
748
749 auth_pgsql_cleanup();
750
751 if (do_connect())
752 {
753 free(querybuf);
754 return (0);
755 }
756
757 pgresult = PQexec(pgconn, querybuf);
758 if (!pgresult || PQresultStatus(pgresult) != PGRES_TUPLES_OK)
759 {
760 DPRINTF("PQexec failed second time, giving up: %s", PQerrorMessage(pgconn));
761 if (pgresult) PQclear(pgresult);
762 free(querybuf);
763 auth_pgsql_cleanup();
764 /* Server went down, that's OK,
765 ** try again next time.
766 */
767 return (0);
768 }
769 }
770 free(querybuf);
771
772 if (PQntuples(pgresult)>0)
773 {
774 char *t, *endp;
775 int num_fields = PQnfields(pgresult);
776
777 if (num_fields < 6)
778 {
779 DPRINTF("incomplete row, only %d fields returned",
780 num_fields);
781 PQclear(pgresult);
782 return 0;
783 }
784
785 t=PQgetvalue(pgresult,0,0);
786 if (t && t[0]) ui.username=strdup(t);
787 t=PQgetvalue(pgresult,0,1);
788 if (t && t[0]) ui.cryptpw=strdup(t);
789 t=PQgetvalue(pgresult,0,2);
790 if (t && t[0]) ui.clearpw=strdup(t);
791 t=PQgetvalue(pgresult,0,3);
792 if (!t || !t[0] ||
793 (ui.uid=strtol(t, &endp, 10), endp[0] != '\0'))
794 {
795 DPRINTF("invalid value for uid: '%s'",
796 t ? t : "<null>");
797 PQclear(pgresult);
798 return 0;
799 }
800 t=PQgetvalue(pgresult,0,4);
801 if (!t || !t[0] ||
802 (ui.gid=strtol(t, &endp, 10), endp[0] != '\0'))
803 {
804 DPRINTF("invalid value for gid: '%s'",
805 t ? t : "<null>");
806 PQclear(pgresult);
807 return 0;
808 }
809 t=PQgetvalue(pgresult,0,5);
810 if (t && t[0])
811 ui.home=strdup(t);
812 else
813 {
814 DPRINTF("required value for 'home' (column 6) is missing");
815 PQclear(pgresult);
816 return 0;
817 }
818 t=num_fields > 6 ? PQgetvalue(pgresult,0,6) : 0;
819 if (t && t[0]) ui.maildir=strdup(t);
820 t=num_fields > 7 ? PQgetvalue(pgresult,0,7) : 0;
821 if (t && t[0]) ui.quota=strdup(t);
822 t=num_fields > 8 ? PQgetvalue(pgresult,0,8) : 0;
823 if (t && t[0]) ui.fullname=strdup(t);
824 t=num_fields > 9 ? PQgetvalue(pgresult,0,9) : 0;
825 if (t && t[0]) ui.options=strdup(t);
826 }
827 else
828 {
829 DPRINTF("zero rows returned");
830 PQclear(pgresult);
831 return (&ui);
832 }
833 PQclear(pgresult);
834
835 return (&ui);
836}
837
838int auth_pgsql_setpass(const char *user, const char *pass,
839 const char *oldpass)
840{
841 char *newpass_crypt;
d9898ee8 842 char *sql_buf;
8d138742
CE
843 size_t sql_buf_size;
844 char dummy_buf[1];
d9898ee8 845 int rc=0;
846
8d138742
CE
847 char *clear_escaped;
848 char *crypt_escaped;
849 int *error = NULL;
850
851 char *username_escaped;
852
d9898ee8 853 const char *clear_field=NULL;
854 const char *crypt_field=NULL;
855 const char *defdomain=NULL;
856 const char *where_clause=NULL;
857 const char *user_table=NULL;
858 const char *login_field=NULL;
859 const char *chpass_clause=NULL; /* tom@minnesota.com */
860
861 if (!pgconn)
862 return (-1);
863
864
865 if (!(newpass_crypt=authcryptpasswd(pass, oldpass)))
866 return (-1);
867
8d138742
CE
868 clear_escaped=malloc(strlen(pass)*2+1);
869
870 if (!clear_escaped)
871 {
872 perror("malloc");
873 free(newpass_crypt);
874 return -1;
875 }
876
877 crypt_escaped=malloc(strlen(newpass_crypt)*2+1);
878
879 if (!crypt_escaped)
880 {
881 perror("malloc");
882 free(clear_escaped);
883 free(newpass_crypt);
884 return -1;
885 }
886
887 PQescapeStringConn(pgconn, clear_escaped, pass, strlen(pass), error);
888 PQescapeStringConn(pgconn, crypt_escaped,
889 newpass_crypt, strlen(newpass_crypt), error);
890
891
d9898ee8 892
893 /* tom@minnesota.com */
894 chpass_clause=read_env("PGSQL_CHPASS_CLAUSE");
895 defdomain=read_env("DEFAULT_DOMAIN");
896 user_table=read_env("PGSQL_USER_TABLE");
897 if (!chpass_clause)
898 {
899 login_field = read_env("PGSQL_LOGIN_FIELD");
900 if (!login_field) login_field = "id";
901 crypt_field=read_env("PGSQL_CRYPT_PWFIELD");
902 clear_field=read_env("PGSQL_CLEAR_PWFIELD");
903 where_clause=read_env("PGSQL_WHERE_CLAUSE");
8d138742
CE
904
905 username_escaped=get_username_escaped(user, defdomain);
906
907 if (!username_escaped)
908 return -1;
909
910 if (!where_clause)
911 where_clause="";
912
913 if (!crypt_field)
914 crypt_field="";
915
916 if (!clear_field)
917 clear_field="";
918
919#define DEFAULT_SETPASS_UPDATE \
920 "UPDATE %s SET %s%s%s%s %s %s%s%s%s WHERE %s='%s' %s%s%s", \
921 user_table, \
922 *clear_field ? clear_field:"", \
923 *clear_field ? "='":"", \
924 *clear_field ? clear_escaped:"", \
925 *clear_field ? "'":"", \
926 \
927 *clear_field && *crypt_field ? ",":"", \
928 \
929 *crypt_field ? crypt_field:"", \
930 *crypt_field ? "='":"", \
931 *crypt_field ? crypt_escaped:"", \
932 *crypt_field ? "'":"", \
933 \
934 login_field, username_escaped, \
935 *where_clause ? " AND (":"", where_clause, \
936 *where_clause ? ")":""
937
938
939 sql_buf_size=snprintf(dummy_buf, 1, DEFAULT_SETPASS_UPDATE);
940
941 sql_buf=malloc(sql_buf_size+1);
942
943 if (sql_buf)
944 snprintf(sql_buf, sql_buf_size+1,
945 DEFAULT_SETPASS_UPDATE);
946
947 free(username_escaped);
d9898ee8 948 }
949 else
950 {
951 sql_buf=parse_chpass_clause(chpass_clause,
952 user,
953 defdomain,
954 pass,
955 newpass_crypt);
956 }
957
958 if (!sql_buf)
959 {
8d138742 960 free(clear_escaped);
d9898ee8 961 free(newpass_crypt);
962 return (-1);
963 }
d9898ee8 964 if (courier_authdebug_login_level >= 2)
965 {
966 DPRINTF("setpass SQL: %s", sql_buf);
967 }
968 pgresult=PQexec (pgconn, sql_buf);
969 if (!pgresult || PQresultStatus(pgresult) != PGRES_COMMAND_OK)
970 {
971 DPRINTF("setpass SQL failed");
972 rc= -1;
973 auth_pgsql_cleanup();
974 }
975 PQclear(pgresult);
8d138742
CE
976 free(clear_escaped);
977 free(crypt_escaped);
978 free(newpass_crypt);
d9898ee8 979 free(sql_buf);
980 return (rc);
981}
982
983void auth_pgsql_enumerate( void(*cb_func)(const char *name,
984 uid_t uid,
985 gid_t gid,
986 const char *homedir,
987 const char *maildir,
988 const char *options,
989 void *void_arg),
990 void *void_arg)
991{
992 const char *select_clause, *defdomain;
8d138742 993 char *querybuf;
d9898ee8 994
8d138742 995 int i,n;
d9898ee8 996
b0322a85
CE
997 if (do_connect())
998 {
999 (*cb_func)(NULL, 0, 0, NULL, NULL, NULL, void_arg);
1000 return;
1001 }
d9898ee8 1002
1003 initui();
1004
1005 select_clause=read_env("PGSQL_ENUMERATE_CLAUSE");
1006 defdomain=read_env("DEFAULT_DOMAIN");
1007 if (!defdomain || !defdomain[0])
1008 defdomain="*"; /* otherwise parse_select_clause fails */
1009
1010 if (!select_clause) /* tom@minnesota.com */
1011 {
1012 const char *user_table,
1013 *uid_field,
1014 *gid_field,
1015 *login_field,
1016 *home_field,
1017 *maildir_field,
1018 *options_field,
1019 *where_clause;
8d138742
CE
1020 char dummy_buf[1];
1021 size_t query_len;
d9898ee8 1022
1023 user_table=read_env("PGSQL_USER_TABLE");
1024
1025 if (!user_table)
1026 {
1027 err("authpgsql: PGSQL_USER_TABLE not set in "
1028 AUTHPGSQLRC ".");
1029 return;
1030 }
1031
1032 uid_field = read_env("PGSQL_UID_FIELD");
1033 if (!uid_field) uid_field = "uid";
1034
1035 gid_field = read_env("PGSQL_GID_FIELD");
1036 if (!gid_field) gid_field = "gid";
1037
1038 login_field = read_env("PGSQL_LOGIN_FIELD");
1039 if (!login_field) login_field = "id";
1040
1041 home_field = read_env("PGSQL_HOME_FIELD");
1042 if (!home_field) home_field = "home";
1043
1044 maildir_field=read_env("PGSQL_MAILDIR_FIELD");
1045 if (!maildir_field) maildir_field="''";
1046
1047 options_field=read_env("PGSQL_AUXOPTIONS_FIELD");
1048 if (!options_field) options_field="''";
1049
1050 where_clause=read_env("PGSQL_WHERE_CLAUSE");
1051 if (!where_clause) where_clause = "";
1052
8d138742
CE
1053#define DEFAULT_ENUMERATE_QUERY \
1054 "SELECT %s, %s, %s, %s, %s, %s FROM %s %s%s",\
1055 login_field, uid_field, gid_field, \
1056 home_field, maildir_field, \
1057 options_field, user_table, \
1058 *where_clause ? " WHERE ":"", \
1059 where_clause
1060
1061
1062 query_len=snprintf(dummy_buf, 1, DEFAULT_ENUMERATE_QUERY);
1063
1064 querybuf=malloc(query_len+1);
d9898ee8 1065
1066 if (!querybuf)
1067 {
1068 perror("malloc");
1069 return;
1070 }
1071
8d138742 1072 snprintf(querybuf, query_len+1, DEFAULT_ENUMERATE_QUERY);
d9898ee8 1073 }
1074 else
1075 {
1076 /* tom@minnesota.com */
1077 querybuf=parse_select_clause (select_clause, "*",
1078 defdomain, "enumerate");
1079 if (!querybuf)
1080 {
1081 DPRINTF("authpgsql: parse_select_clause failed");
1082 return;
1083 }
1084 }
1085 DPRINTF("authpgsql: enumerate query: %s", querybuf);
1086
1087 if (PQsendQuery(pgconn, querybuf) == 0)
1088 {
1089 DPRINTF("PQsendQuery failed, reconnecting: %s",PQerrorMessage(pgconn));
1090
1091 auth_pgsql_cleanup();
1092
1093 if (do_connect())
1094 {
1095 free(querybuf);
1096 return;
1097 }
1098
1099 if (PQsendQuery(pgconn, querybuf) == 0)
1100 {
1101 DPRINTF("PQsendQuery failed second time, giving up: %s",PQerrorMessage(pgconn));
1102 free(querybuf);
1103 auth_pgsql_cleanup();
1104 return;
1105 }
1106 }
1107 free(querybuf);
1108
1109 while ((pgresult = PQgetResult(pgconn)) != NULL)
1110 {
1111 if (PQresultStatus(pgresult) != PGRES_TUPLES_OK)
1112 {
1113 DPRINTF("pgsql error during enumeration: %s",PQerrorMessage(pgconn));
1114 PQclear(pgresult);
1115 return;
1116 }
1117
1118 for (n=PQntuples(pgresult), i=0; i<n; i++)
1119 {
1120 const char *username;
1121 uid_t uid;
1122 gid_t gid;
1123 const char *homedir;
1124 const char *maildir;
1125 const char *options;
1126
1127 username=PQgetvalue(pgresult,i,0);
1128 uid=atol(PQgetvalue(pgresult,i,1));
1129 gid=atol(PQgetvalue(pgresult,i,2));
1130 homedir=PQgetvalue(pgresult,i,3);
1131 maildir=PQgetvalue(pgresult,i,4);
1132 options=PQgetvalue(pgresult,i,5);
1133
1134 if (!username || !*username || !homedir || !*homedir)
1135 continue;
1136
1137 if (maildir && !*maildir)
1138 maildir=NULL;
1139
1140 (*cb_func)(username, uid, gid, homedir,
1141 maildir, options, void_arg);
1142
1143 }
1144 PQclear(pgresult);
1145 }
1146 /* FIXME: is it possible that a NULL result from PQgetResult could
1147 indicate an error rather than EOF? The documentation is not clear */
1148 (*cb_func)(NULL, 0, 0, NULL, NULL, NULL, void_arg);
1149}