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:
sentquery(const authpgsql_connection & conn,const std::string & query)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 
operator bool() const65 		operator bool() const
66 		{
67 			return status != 0;
68 		}
69 	};
70 
71 	class result {
72 		PGresult *res;
73 
74 	public:
result(const authpgsql_connection & conn,const std::string & query)75 		result(const authpgsql_connection &conn,
76 		       const std::string &query)
77 			: res(PQexec(conn.pgconn, query.c_str()))
78 		{
79 		}
80 
result(const authpgsql_connection & conn,const sentquery & query)81 		result(const authpgsql_connection &conn,
82 		       const sentquery &query)
83 			: res(PQgetResult(conn.pgconn))
84 		{
85 		}
86 
~result()87 		~result()
88 		{
89 			if (res)
90 				PQclear(res);
91 		}
92 
93 		result(const result &res);
94 		result &operator=(const result &res);
95 
operator bool() const96 		operator bool() const { return res != 0; }
97 
query_failed() const98 		bool query_failed() const
99 		{
100 			return res == 0
101 				|| PQresultStatus(res) != PGRES_TUPLES_OK;
102 		}
103 
command_failed() const104 		bool command_failed() const
105 		{
106 			return res == 0
107 				|| PQresultStatus(res) != PGRES_COMMAND_OK;
108 		}
109 
ntuples() const110 		size_t ntuples() const
111 		{
112 			return res == 0 ? 0: PQntuples(res);
113 		}
114 
nfields() const115 		size_t nfields() const
116 		{
117 			return res == 0 ? 0: PQnfields(res);
118 		}
119 
value(size_t tuple,size_t field) const120 		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 
operator =(const authpgsqlrc_file & o)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 
authpgsqlrc_file(authpgsql_connection & connArg)174 		authpgsqlrc_file(authpgsql_connection &connArg)
175 			: courier::auth::config_file(AUTHPGSQLRC),
176 			  conn(connArg)
177 		{
178 		}
179 
do_load()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 
do_reload()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 
authpgsql_connection()252 	authpgsql_connection()
253 		: last_time(0), pgconn(0), config_file(*this)
254 	{
255 	}
256 
~authpgsql_connection()257 	~authpgsql_connection()
258 	{
259 		disconnect();
260 	}
261 
disconnect()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 
escape(const std::string & s)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 
escape_username(std::string username)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 
do_connect()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 
getuserinfo(authpgsql_userinfo & uiret,const char * username,const char * service)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 
getuserinfo(authpgsql_userinfo & uiret,const result & res)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 
setpass(const char * user,const char * pass,const char * oldpass)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 
enumerate(void (* cb_func)(const char * name,uid_t uid,gid_t gid,const char * homedir,const char * maildir,const char * options,void * void_arg),void * void_arg)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 
enumerate(const sentquery & sent,void (* cb_func)(const char * name,uid_t uid,gid_t gid,const char * homedir,const char * maildir,const char * options,void * void_arg),void * void_arg)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 
get_conn()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 
auth_pgsql_getuserinfo(authpgsql_userinfo & uiret,const char * username,const char * service)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 
docheckpw(authpgsql_userinfo & authinfo,const char * pass)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 
do_auth_pgsql_login(const char * service,char * authdata,int (* callback_func)(struct authinfo *,void *),void * callback_arg)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 
do_auth_pgsql_changepw(const char * service,const char * user,const char * pass,const char * newpass)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 
auth_pgsql_cleanup()869 void auth_pgsql_cleanup()
870 {
871 	if (authpgsql_connection::singleton)
872 		delete authpgsql_connection::singleton;
873 	authpgsql_connection::singleton=0;
874 }
875 
auth_pgsql_enumerate(void (* cb_func)(const char * name,uid_t uid,gid_t gid,const char * homedir,const char * maildir,const char * options,void * void_arg),void * void_arg)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 
auth_pgsql_login(const char * service,char * authdata,int (* callback_func)(struct authinfo *,void *),void * callback_arg)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 
auth_pgsql_changepw(const char * service,const char * user,const char * pass,const char * newpass)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 
auth_pgsql_pre(const char * user,const char * service,int (* callback)(struct authinfo *,void *),void * arg)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 };
939