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