Commit | Line | Data |
---|---|---|
0e333c05 CE |
1 | /* |
2 | ** Copyright 2000-2016 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 | #include <errno.h> | |
17 | ||
18 | extern "C" { | |
19 | #include "authpgsql.h" | |
20 | #include "authpgsqlrc.h" | |
21 | #include "auth.h" | |
22 | #include "courierauthdebug.h" | |
23 | }; | |
24 | ||
25 | #include "authconfigfile.h" | |
26 | #include <string> | |
27 | ||
28 | #define err courier_auth_err | |
29 | ||
30 | ||
31 | class authpgsql_userinfo { | |
32 | public: | |
33 | std::string username; | |
34 | std::string fullname; | |
35 | std::string cryptpw; | |
36 | std::string clearpw; | |
37 | std::string home; | |
38 | std::string maildir; | |
39 | std::string quota; | |
40 | std::string options; | |
41 | uid_t uid; | |
42 | gid_t gid; | |
43 | } ; | |
44 | ||
45 | class authpgsql_connection { | |
46 | ||
47 | time_t last_time; | |
48 | PGconn *pgconn; | |
49 | public: | |
50 | ||
51 | class sentquery { | |
52 | ||
53 | int status; | |
54 | ||
55 | public: | |
56 | sentquery(const authpgsql_connection &conn, | |
57 | const std::string &query) | |
58 | : status(PQsendQuery(conn.pgconn, query.c_str())) | |
59 | { | |
60 | if (status == 0) | |
61 | DPRINTF("PQsendQuery failed: %s", | |
62 | PQerrorMessage(conn.pgconn)); | |
63 | } | |
64 | ||
65 | operator bool() const | |
66 | { | |
67 | return status != 0; | |
68 | } | |
69 | }; | |
70 | ||
71 | class result { | |
72 | PGresult *res; | |
73 | ||
74 | public: | |
75 | result(const authpgsql_connection &conn, | |
76 | const std::string &query) | |
77 | : res(PQexec(conn.pgconn, query.c_str())) | |
78 | { | |
79 | } | |
80 | ||
81 | result(const authpgsql_connection &conn, | |
82 | const sentquery &query) | |
83 | : res(PQgetResult(conn.pgconn)) | |
84 | { | |
85 | } | |
86 | ||
87 | ~result() | |
88 | { | |
89 | if (res) | |
90 | PQclear(res); | |
91 | } | |
92 | ||
93 | result(const result &res); | |
94 | result &operator=(const result &res); | |
95 | ||
96 | operator bool() const { return res != 0; } | |
97 | ||
98 | bool query_failed() const | |
99 | { | |
100 | return res == 0 | |
101 | || PQresultStatus(res) != PGRES_TUPLES_OK; | |
102 | } | |
103 | ||
104 | bool command_failed() const | |
105 | { | |
106 | return res == 0 | |
107 | || PQresultStatus(res) != PGRES_COMMAND_OK; | |
108 | } | |
109 | ||
110 | size_t ntuples() const | |
111 | { | |
112 | return res == 0 ? 0: PQntuples(res); | |
113 | } | |
114 | ||
115 | size_t nfields() const | |
116 | { | |
117 | return res == 0 ? 0: PQnfields(res); | |
118 | } | |
119 | ||
120 | std::string value(size_t tuple, size_t field) const | |
121 | { | |
122 | std::string v; | |
123 | ||
124 | if (tuple < ntuples() && field < nfields()) | |
125 | { | |
126 | const char *p=PQgetvalue(res, tuple, field); | |
127 | ||
128 | if (p) | |
129 | v=p; | |
130 | } | |
131 | return v; | |
132 | } | |
133 | }; | |
134 | ||
135 | class authpgsqlrc_vars { | |
136 | ||
137 | public: | |
138 | ||
139 | std::string character_set; | |
140 | std::string connection; | |
141 | std::string select_clause; | |
142 | std::string chpass_clause; | |
143 | std::string enumerate_clause; | |
144 | std::string defdomain; | |
145 | std::string user_table; | |
146 | std::string clear_field; | |
147 | std::string crypt_field; | |
148 | std::string name_field; | |
149 | std::string uid_field; | |
150 | std::string gid_field; | |
151 | std::string login_field; | |
152 | std::string home_field; | |
153 | std::string maildir_field; | |
154 | std::string defaultdelivery_field; | |
155 | std::string quota_field; | |
156 | std::string options_field; | |
157 | std::string where_clause; | |
158 | }; | |
159 | ||
160 | class authpgsqlrc_file : public courier::auth::config_file, | |
161 | public authpgsqlrc_vars { | |
162 | ||
163 | authpgsql_connection &conn; | |
164 | ||
165 | public: | |
166 | ||
167 | authpgsqlrc_file &operator=(const authpgsqlrc_file &o) | |
168 | { | |
169 | courier::auth::config_file::operator=(o); | |
170 | authpgsqlrc_vars::operator=(o); | |
171 | return *this; | |
172 | } | |
173 | ||
174 | authpgsqlrc_file(authpgsql_connection &connArg) | |
175 | : courier::auth::config_file(AUTHPGSQLRC), | |
176 | conn(connArg) | |
177 | { | |
178 | } | |
179 | ||
180 | bool do_load() | |
181 | { | |
182 | character_set=config("PGSQL_CHARACTER_SET"); | |
183 | ||
184 | if (!config("PGSQL_CONNECTION", connection, true)) | |
185 | return false; | |
186 | ||
187 | select_clause=config("PGSQL_SELECT_CLAUSE"); | |
188 | chpass_clause=config("PGSQL_CHPASS_CLAUSE"); | |
189 | enumerate_clause=config("PGSQL_ENUMERATE_CLAUSE"); | |
190 | ||
191 | defdomain=config("DEFAULT_DOMAIN"); | |
192 | ||
193 | if (select_clause.empty() || chpass_clause.empty() || | |
194 | enumerate_clause.empty()) | |
195 | { | |
196 | if (!config("PGSQL_USER_TABLE", user_table, true)) | |
197 | return false; | |
198 | ||
199 | clear_field=config("PGSQL_CLEAR_PWFIELD"); | |
200 | if (clear_field.empty()) | |
201 | { | |
202 | if (!config("PGSQL_CRYPT_PWFIELD", | |
203 | crypt_field, | |
204 | true)) | |
205 | return false; | |
206 | } | |
207 | else | |
208 | { | |
209 | crypt_field=config("PGSQL_CRYPT_PWFIELD"); | |
210 | } | |
211 | ||
212 | config("PGSQL_NAME_FIELD", name_field, false, | |
213 | "''"); | |
214 | ||
215 | if (crypt_field.empty()) crypt_field="''"; | |
216 | if (clear_field.empty()) clear_field="''"; | |
217 | ||
218 | config("PGSQL_UID_FIELD", uid_field, false, "uid"); | |
219 | config("PGSQL_GID_FIELD", gid_field, false, "gid"); | |
220 | config("PGSQL_LOGIN_FIELD", login_field, false, "id"); | |
221 | config("PGSQL_HOME_FIELD", home_field, false, "home"); | |
222 | config("PGSQL_MAILDIR_FIELD", maildir_field, false, "''"); | |
223 | config("PGSQL_DEFAULTDELIVERY", defaultdelivery_field, false, "''"); | |
224 | config("PGSQL_QUOTA_FIELD", quota_field, false, "''"); | |
225 | config("PGSQL_AUXOPTIONS_FIELD", options_field, false, "''"); | |
226 | ||
227 | config("PGSQL_WHERE_CLAUSE", where_clause, false, "1=1"); | |
228 | } | |
229 | ||
230 | return true; | |
231 | } | |
232 | ||
233 | void do_reload() | |
234 | { | |
235 | authpgsqlrc_file new_file(conn); | |
236 | ||
237 | if (new_file.load(true)) | |
238 | { | |
239 | *this=new_file; | |
240 | DPRINTF("authpgsql: reloaded %s", filename); | |
241 | ||
242 | // Disconnect from the server, new login | |
243 | // parameters, perhaps. | |
244 | ||
245 | conn.disconnect(); | |
246 | } | |
247 | } | |
248 | }; | |
249 | ||
250 | authpgsqlrc_file config_file; | |
251 | ||
252 | authpgsql_connection() | |
253 | : last_time(0), pgconn(0), config_file(*this) | |
254 | { | |
255 | } | |
256 | ||
257 | ~authpgsql_connection() | |
258 | { | |
259 | disconnect(); | |
260 | } | |
261 | ||
262 | void disconnect() | |
263 | { | |
264 | if (pgconn) | |
265 | { | |
266 | PQfinish(pgconn); | |
267 | pgconn=0; | |
268 | } | |
269 | } | |
270 | ||
271 | bool do_connect(); | |
272 | ||
273 | bool getuserinfo(authpgsql_userinfo &uiret, | |
274 | const char *username, | |
275 | const char *service); | |
276 | ||
277 | private: | |
278 | bool getuserinfo(authpgsql_userinfo &uiret, | |
279 | const result &res); | |
280 | public: | |
281 | bool setpass(const char *user, const char *pass, const char *oldpass); | |
282 | ||
283 | void enumerate( void(*cb_func)(const char *name, | |
284 | uid_t uid, | |
285 | gid_t gid, | |
286 | const char *homedir, | |
287 | const char *maildir, | |
288 | const char *options, | |
289 | void *void_arg), | |
290 | void *void_arg); | |
291 | void enumerate( const sentquery &sent, | |
292 | void(*cb_func)(const char *name, | |
293 | uid_t uid, | |
294 | gid_t gid, | |
295 | const char *homedir, | |
296 | const char *maildir, | |
297 | const char *options, | |
298 | void *void_arg), | |
299 | void *void_arg); | |
300 | ||
301 | std::string escape(const std::string &s) | |
302 | { | |
303 | std::string buffer; | |
304 | size_t n=s.size()*2+1; | |
305 | ||
306 | buffer.resize(n); | |
307 | ||
308 | n=PQescapeStringConn(pgconn, &buffer[0], | |
309 | s.c_str(), s.size(), 0); | |
310 | ||
311 | buffer.resize(n); | |
312 | ||
313 | return buffer; | |
314 | } | |
315 | ||
316 | std::string escape_username(std::string username) | |
317 | { | |
318 | if (username.find('@') == username.npos && | |
319 | !config_file.defdomain.empty()) | |
320 | { | |
321 | username.push_back('@'); | |
322 | username += config_file.defdomain; | |
323 | } | |
324 | ||
325 | return escape(username); | |
326 | } | |
327 | ||
328 | static authpgsql_connection *singleton; | |
329 | }; | |
330 | ||
331 | bool authpgsql_connection::do_connect() | |
332 | { | |
333 | if (pgconn) | |
334 | { | |
335 | time_t t_check; | |
336 | ||
337 | time(&t_check); | |
338 | ||
339 | if (t_check < last_time) | |
340 | last_time=t_check; /* System clock changed */ | |
341 | ||
342 | if (t_check < last_time + 60) | |
343 | return true; | |
344 | ||
345 | last_time=t_check; | |
346 | ||
347 | if (PQstatus(pgconn) == CONNECTION_OK) return true; | |
348 | ||
349 | DPRINTF("authpgsql: PQstatus failed, connection lost"); | |
350 | PQfinish(pgconn); | |
351 | pgconn=0; | |
352 | } | |
353 | ||
354 | pgconn = PQconnectdb(config_file.connection.c_str()); | |
355 | ||
356 | if (PQstatus(pgconn) == CONNECTION_BAD) | |
357 | { | |
358 | err("PGSQL_CONNECTION could not be established"); | |
359 | err("%s", PQerrorMessage(pgconn)); | |
360 | PQfinish(pgconn); | |
361 | pgconn=0; | |
362 | return false; | |
363 | } | |
364 | ||
365 | if (!config_file.character_set.empty()) | |
366 | { | |
367 | PQsetClientEncoding(pgconn, | |
368 | config_file.character_set.c_str()); | |
369 | std::string real_character_set= | |
370 | pg_encoding_to_char(PQclientEncoding(pgconn)); | |
371 | ||
372 | if (config_file.character_set != real_character_set) | |
373 | { | |
374 | err("Cannot set character set to \"%s\"," | |
375 | " using \"%s\"\n", | |
376 | config_file.character_set.c_str(), | |
377 | real_character_set.c_str()); | |
378 | } | |
379 | else | |
380 | { | |
381 | DPRINTF("Using character set: %s", | |
382 | config_file.character_set.c_str()); | |
383 | } | |
384 | } | |
385 | ||
386 | return true; | |
387 | } | |
388 | ||
389 | bool authpgsql_connection::getuserinfo(authpgsql_userinfo &uiret, | |
390 | const char *username, | |
391 | const char *service) | |
392 | { | |
393 | std::string querybuf; | |
394 | ||
395 | if (!do_connect()) | |
396 | return false; | |
397 | ||
398 | if (config_file.select_clause.empty()) | |
399 | { | |
400 | std::ostringstream o; | |
401 | ||
402 | o << "SELECT " | |
403 | << config_file.login_field << ", " | |
404 | << config_file.crypt_field << ", " | |
405 | << config_file.clear_field << ", " | |
406 | << config_file.uid_field << ", " | |
407 | << config_file.gid_field << ", " | |
408 | << config_file.home_field << ", " | |
409 | << (strcmp(service, "courier") == 0 ? | |
410 | config_file.defaultdelivery_field | |
411 | :config_file.maildir_field) << ", " | |
412 | << config_file.quota_field << ", " | |
413 | << config_file.name_field << ", " | |
414 | << config_file.options_field | |
415 | << " FROM " << config_file.user_table | |
416 | << " WHERE " << config_file.login_field | |
417 | << " = '" | |
418 | << escape_username(username) | |
419 | << "' AND (" << config_file.where_clause << ")"; | |
420 | ||
421 | querybuf=o.str(); | |
422 | } | |
423 | else | |
424 | { | |
425 | std::map<std::string, std::string> parameters; | |
426 | ||
427 | parameters["service"]=service; | |
428 | ||
429 | querybuf=config_file | |
430 | .parse_custom_query(config_file.select_clause, | |
431 | escape(username), | |
432 | config_file.defdomain, | |
433 | parameters); | |
434 | } | |
435 | ||
436 | DPRINTF("SQL query: %s", querybuf.c_str()); | |
437 | ||
438 | result res1(*this, querybuf); | |
439 | ||
440 | if (res1) | |
441 | return getuserinfo(uiret, res1); | |
442 | ||
443 | disconnect(); | |
444 | if (do_connect()) | |
445 | { | |
446 | result res2(*this, querybuf); | |
447 | ||
448 | if (res2) | |
449 | return getuserinfo(uiret, res2); | |
450 | } | |
451 | return false; | |
452 | } | |
453 | ||
454 | bool authpgsql_connection::getuserinfo(authpgsql_userinfo &uiret, | |
455 | const result &res) | |
456 | { | |
457 | if (res.query_failed()) | |
458 | return false; | |
459 | ||
460 | if (res.ntuples() > 0) | |
461 | { | |
462 | if (res.nfields() < 6) | |
463 | { | |
464 | DPRINTF("incomplete row, only %d fields returned", | |
465 | res.nfields()); | |
466 | return false; | |
467 | } | |
468 | ||
469 | uiret.username=res.value(0, 0); | |
470 | uiret.cryptpw=res.value(0, 1); | |
471 | uiret.clearpw=res.value(0, 2); | |
472 | ||
473 | { | |
474 | std::string v=res.value(0, 3); | |
475 | ||
476 | std::istringstream i(v); | |
477 | ||
478 | i >> uiret.uid; | |
479 | ||
480 | if (i.fail() || !i.eof()) | |
481 | { | |
482 | DPRINTF("invalid value for uid: '%s'", | |
483 | v.c_str()); | |
484 | return false; | |
485 | } | |
486 | } | |
487 | ||
488 | { | |
489 | std::string v=res.value(0, 4); | |
490 | ||
491 | std::istringstream i(v); | |
492 | ||
493 | i >> uiret.gid; | |
494 | ||
495 | if (i.fail() || !i.eof()) | |
496 | { | |
497 | DPRINTF("invalid value for gid: '%s'", | |
498 | v.c_str()); | |
499 | return false; | |
500 | } | |
501 | } | |
502 | ||
503 | ||
504 | uiret.home=res.value(0, 5); | |
505 | uiret.maildir=res.value(0, 6); | |
506 | uiret.quota=res.value(0, 7); | |
507 | uiret.fullname=res.value(0, 8); | |
508 | uiret.options=res.value(0, 9); | |
509 | } | |
510 | else | |
511 | { | |
512 | DPRINTF("zero rows returned"); | |
513 | return true; | |
514 | } | |
515 | ||
516 | return true; | |
517 | } | |
518 | ||
519 | bool authpgsql_connection::setpass(const char *user, const char *pass, | |
520 | const char *oldpass) | |
521 | { | |
522 | if (!do_connect()) | |
523 | return false; | |
524 | ||
525 | std::string newpass_crypt; | |
526 | ||
527 | { | |
528 | char *p; | |
529 | ||
530 | if (!(p=authcryptpasswd(pass, oldpass))) | |
531 | return false; | |
532 | ||
533 | newpass_crypt=p; | |
534 | free(p); | |
535 | } | |
536 | ||
537 | std::string clear_escaped=escape(pass); | |
538 | std::string crypt_escaped=escape(newpass_crypt); | |
539 | ||
540 | std::string sql_buf; | |
541 | ||
542 | if (config_file.chpass_clause.empty()) | |
543 | { | |
544 | std::ostringstream o; | |
545 | ||
546 | o << "UPDATE " << config_file.user_table | |
547 | << " SET "; | |
548 | if (config_file.clear_field != "''") | |
549 | { | |
550 | o << config_file.clear_field << "='" | |
551 | << clear_escaped | |
552 | << "'"; | |
553 | ||
554 | if (config_file.crypt_field != "''") | |
555 | o << ", "; | |
556 | } | |
557 | ||
558 | if (config_file.crypt_field != "''") | |
559 | { | |
560 | o << config_file.crypt_field << "='" | |
561 | << crypt_escaped | |
562 | << "'"; | |
563 | } | |
564 | ||
565 | o << " WHERE " | |
566 | << config_file.login_field << "='" | |
567 | << escape_username(user) | |
568 | << "' AND (" << config_file.where_clause << ")"; | |
569 | sql_buf=o.str(); | |
570 | } | |
571 | else | |
572 | { | |
573 | std::map<std::string, std::string> parameters; | |
574 | ||
575 | parameters["newpass"]=clear_escaped; | |
576 | parameters["newpass_crypt"]=crypt_escaped; | |
577 | ||
578 | sql_buf=config_file | |
579 | .parse_custom_query(config_file.chpass_clause, | |
580 | user, | |
581 | config_file.defdomain, | |
582 | parameters); | |
583 | } | |
584 | ||
585 | if (courier_authdebug_login_level >= 2) | |
586 | { | |
587 | DPRINTF("setpass SQL: %s", sql_buf.c_str()); | |
588 | } | |
589 | ||
590 | result res(*this, sql_buf); | |
591 | ||
592 | if (res.command_failed()) | |
593 | { | |
594 | DPRINTF("setpass SQL failed"); | |
595 | disconnect(); | |
596 | return false; | |
597 | } | |
598 | return (true); | |
599 | } | |
600 | ||
601 | void authpgsql_connection::enumerate( void(*cb_func)(const char *name, | |
602 | uid_t uid, | |
603 | gid_t gid, | |
604 | const char *homedir, | |
605 | const char *maildir, | |
606 | const char *options, | |
607 | void *void_arg), | |
608 | void *void_arg) | |
609 | { | |
610 | if (!do_connect()) | |
611 | { | |
612 | (*cb_func)(NULL, 0, 0, NULL, NULL, NULL, void_arg); | |
613 | return; | |
614 | } | |
615 | ||
616 | std::string sql_buf; | |
617 | ||
618 | if (config_file.enumerate_clause.empty()) | |
619 | { | |
620 | std::ostringstream o; | |
621 | ||
622 | o << "SELECT " | |
623 | << config_file.login_field << ", " | |
624 | << config_file.uid_field << ", " | |
625 | << config_file.gid_field << ", " | |
626 | << config_file.home_field << ", " | |
627 | << config_file.maildir_field << ", " | |
628 | << config_file.options_field | |
629 | << " FROM " | |
630 | << config_file.user_table << " WHERE " | |
631 | << config_file.where_clause; | |
632 | ||
633 | sql_buf=o.str(); | |
634 | } | |
635 | else | |
636 | { | |
637 | std::map<std::string, std::string> parameters; | |
638 | ||
639 | parameters["service"]="enumerate"; | |
640 | sql_buf=config_file | |
641 | .parse_custom_query(config_file.enumerate_clause, "*", | |
642 | config_file.defdomain, parameters); | |
643 | } | |
644 | ||
645 | DPRINTF("authpgsql: enumerate query: %s", sql_buf.c_str()); | |
646 | ||
647 | sentquery query1(*this, sql_buf); | |
648 | ||
649 | if (query1) | |
650 | { | |
651 | enumerate(query1, cb_func, void_arg); | |
652 | return; | |
653 | } | |
654 | ||
655 | disconnect(); | |
656 | ||
657 | if (!do_connect()) | |
658 | return; | |
659 | ||
660 | sentquery query2(*this, sql_buf); | |
661 | if (query2) | |
662 | { | |
663 | enumerate(query2, cb_func, void_arg); | |
664 | return; | |
665 | } | |
666 | } | |
667 | ||
668 | void authpgsql_connection::enumerate( const sentquery &sent, | |
669 | void(*cb_func)(const char *name, | |
670 | uid_t uid, | |
671 | gid_t gid, | |
672 | const char *homedir, | |
673 | const char *maildir, | |
674 | const char *options, | |
675 | void *void_arg), | |
676 | void *void_arg) | |
677 | { | |
678 | while (1) | |
679 | { | |
680 | result res(*this, sent); | |
681 | ||
682 | if (!res) | |
683 | break; | |
684 | ||
685 | if (res.query_failed()) | |
686 | continue; | |
687 | ||
688 | size_t n=res.ntuples(); | |
689 | ||
690 | for (size_t i=0; i<n; ++i) | |
691 | { | |
692 | std::string username=res.value(i, 0); | |
693 | std::string uid_s=res.value(i, 1); | |
694 | std::string gid_s=res.value(i, 2); | |
695 | std::string homedir=res.value(i, 3); | |
696 | std::string maildir=res.value(i, 4); | |
697 | std::string options=res.value(i, 5); | |
698 | ||
699 | uid_t uid=0; | |
700 | gid_t gid=0; | |
701 | ||
702 | std::istringstream(uid_s) >> uid; | |
703 | std::istringstream(gid_s) >> gid; | |
704 | ||
705 | if (username.empty() || homedir.empty()) | |
706 | continue; | |
707 | ||
708 | (*cb_func)(username.c_str(), uid, gid, | |
709 | homedir.c_str(), | |
710 | (maildir.empty() ? 0:maildir.c_str()), | |
711 | (options.empty() ? 0:options.c_str()), | |
712 | void_arg); | |
713 | } | |
714 | } | |
715 | (*cb_func)(NULL, 0, 0, NULL, NULL, NULL, void_arg); | |
716 | } | |
717 | ||
718 | ||
719 | static authpgsql_connection *get_conn() | |
720 | { | |
721 | if (authpgsql_connection::singleton) | |
722 | { | |
723 | authpgsql_connection::singleton->config_file.load(true); | |
724 | return authpgsql_connection::singleton; | |
725 | } | |
726 | ||
727 | authpgsql_connection *new_conn=new authpgsql_connection; | |
728 | ||
729 | if (new_conn->config_file.load()) | |
730 | { | |
731 | authpgsql_connection::singleton=new_conn; | |
732 | return new_conn; | |
733 | } | |
734 | ||
735 | delete new_conn; | |
736 | return NULL; | |
737 | } | |
738 | ||
739 | authpgsql_connection *authpgsql_connection::singleton=0; | |
740 | ||
741 | ||
742 | static bool auth_pgsql_getuserinfo(authpgsql_userinfo &uiret, | |
743 | const char *username, | |
744 | const char *service) | |
745 | { | |
746 | authpgsql_connection *conn=get_conn(); | |
747 | ||
748 | if (!conn) | |
749 | return false; | |
750 | ||
751 | return conn->getuserinfo(uiret, username, service); | |
752 | } | |
753 | ||
754 | static bool docheckpw(authpgsql_userinfo &authinfo, | |
755 | const char *pass) | |
756 | { | |
757 | if (!authinfo.cryptpw.empty()) | |
758 | { | |
759 | if (authcheckpassword(pass,authinfo.cryptpw.c_str())) | |
760 | { | |
761 | errno=EPERM; | |
762 | return false; | |
763 | } | |
764 | } | |
765 | else if (!authinfo.clearpw.empty()) | |
766 | { | |
767 | if (authinfo.clearpw != pass) | |
768 | { | |
769 | if (courier_authdebug_login_level >= 2) | |
770 | { | |
771 | DPRINTF("supplied password '%s' does not match clearpasswd '%s'", | |
772 | pass, authinfo.clearpw.empty()); | |
773 | } | |
774 | else | |
775 | { | |
776 | DPRINTF("supplied password does not match clearpasswd"); | |
777 | } | |
778 | errno=EPERM; | |
779 | return false; | |
780 | } | |
781 | } | |
782 | else | |
783 | { | |
784 | DPRINTF("no password available to compare"); | |
785 | errno=EPERM; | |
786 | return false; | |
787 | } | |
788 | return true; | |
789 | } | |
790 | ||
791 | static int do_auth_pgsql_login(const char *service, char *authdata, | |
792 | int (*callback_func)(struct authinfo *, void *), | |
793 | void *callback_arg) | |
794 | { | |
795 | char *user, *pass; | |
796 | authpgsql_userinfo uiret; | |
797 | struct authinfo aa; | |
798 | ||
799 | if ((user=strtok(authdata, "\n")) == 0 || | |
800 | (pass=strtok(0, "\n")) == 0) | |
801 | { | |
802 | errno=EPERM; | |
803 | return (-1); | |
804 | } | |
805 | ||
806 | if (!auth_pgsql_getuserinfo(uiret, user, service)) | |
807 | { | |
808 | errno=EACCES; /* Fatal error - such as PgSQL being down */ | |
809 | return (-1); | |
810 | } | |
811 | ||
812 | if (!docheckpw(uiret, pass)) | |
813 | return -1; | |
814 | ||
815 | memset(&aa, 0, sizeof(aa)); | |
816 | ||
817 | aa.sysuserid= &uiret.uid; | |
818 | aa.sysgroupid= uiret.gid; | |
819 | aa.homedir=uiret.home.c_str(); | |
820 | aa.maildir=uiret.maildir.empty() ? 0:uiret.maildir.c_str(); | |
821 | aa.address=uiret.username.c_str(); | |
822 | aa.quota=uiret.quota.empty() ? 0:uiret.quota.c_str(); | |
823 | aa.fullname=uiret.fullname.c_str(); | |
824 | aa.options=uiret.options.c_str(); | |
825 | aa.passwd=uiret.cryptpw.empty() ? 0:uiret.cryptpw.c_str(); | |
826 | aa.clearpasswd=pass; | |
827 | courier_authdebug_authinfo("DEBUG: authpgsql: ", &aa, | |
828 | aa.clearpasswd, aa.passwd); | |
829 | return (*callback_func)(&aa, callback_arg); | |
830 | } | |
831 | ||
832 | ||
833 | static int do_auth_pgsql_changepw(const char *service, const char *user, | |
834 | const char *pass, | |
835 | const char *newpass) | |
836 | { | |
837 | authpgsql_connection *conn=get_conn(); | |
838 | ||
839 | if (!conn) | |
840 | return false; | |
841 | ||
842 | authpgsql_userinfo uiret; | |
843 | ||
844 | if (conn->getuserinfo(uiret, user, service)) | |
845 | { | |
846 | if (!docheckpw(uiret, pass)) | |
847 | { | |
848 | errno=EPERM; | |
849 | return -1; | |
850 | } | |
851 | ||
852 | if (!conn->setpass(user, newpass, uiret.cryptpw.c_str())) | |
853 | { | |
854 | errno=EPERM; | |
855 | return (-1); | |
856 | } | |
857 | return 0; | |
858 | } | |
859 | ||
860 | errno=EPERM; | |
861 | return (-1); | |
862 | } | |
863 | ||
864 | extern "C" { | |
865 | #if 0 | |
866 | }; | |
867 | #endif | |
868 | ||
869 | void auth_pgsql_cleanup() | |
870 | { | |
871 | if (authpgsql_connection::singleton) | |
872 | delete authpgsql_connection::singleton; | |
873 | authpgsql_connection::singleton=0; | |
874 | } | |
875 | ||
876 | void auth_pgsql_enumerate( void(*cb_func)(const char *name, | |
877 | uid_t uid, | |
878 | gid_t gid, | |
879 | const char *homedir, | |
880 | const char *maildir, | |
881 | const char *options, | |
882 | void *void_arg), | |
883 | void *void_arg) | |
884 | { | |
885 | authpgsql_connection *conn=get_conn(); | |
886 | ||
887 | if (conn) | |
888 | conn->enumerate(cb_func, void_arg); | |
889 | } | |
890 | ||
891 | int auth_pgsql_login(const char *service, char *authdata, | |
892 | int (*callback_func)(struct authinfo *, void *), | |
893 | void *callback_arg) | |
894 | { | |
895 | return do_auth_pgsql_login(service, authdata, | |
896 | callback_func, | |
897 | callback_arg); | |
898 | } | |
899 | ||
900 | int auth_pgsql_changepw(const char *service, const char *user, | |
901 | const char *pass, | |
902 | const char *newpass) | |
903 | { | |
904 | return do_auth_pgsql_changepw(service, user, pass, newpass); | |
905 | } | |
906 | ||
907 | int auth_pgsql_pre(const char *user, const char *service, | |
908 | int (*callback)(struct authinfo *, void *), void *arg) | |
909 | { | |
910 | struct authinfo aa; | |
911 | authpgsql_userinfo uiret; | |
912 | ||
913 | if (!auth_pgsql_getuserinfo(uiret, user, service)) | |
914 | return 1; | |
915 | ||
916 | if (uiret.home.empty()) /* User not found */ | |
917 | return (-1); | |
918 | ||
919 | memset(&aa, 0, sizeof(aa)); | |
920 | ||
921 | aa.sysuserid= &uiret.uid; | |
922 | aa.sysgroupid= uiret.gid; | |
923 | aa.homedir=uiret.home.c_str(); | |
924 | aa.maildir=uiret.maildir.empty() ? 0:uiret.maildir.c_str(); | |
925 | aa.address=uiret.username.c_str(); | |
926 | aa.quota=uiret.quota.empty() ? 0:uiret.quota.c_str(); | |
927 | aa.fullname=uiret.fullname.c_str(); | |
928 | aa.options=uiret.options.c_str(); | |
929 | aa.passwd=uiret.cryptpw.empty() ? 0:uiret.cryptpw.c_str(); | |
930 | aa.clearpasswd=uiret.clearpw.empty() ? 0:uiret.clearpw.c_str(); | |
931 | ||
932 | return ((*callback)(&aa, arg)); | |
933 | } | |
934 | ||
935 | #if 0 | |
936 | { | |
937 | #endif | |
938 | }; |