1 /*
2  * PAM authentication module for PostgreSQL
3  *
4  * Based in part on pam_unix.c of FreeBSD. See COPYRIGHT
5  * for licensing details.
6  *
7  * David D.W. Downey ("pgpkeys") <david-downey@codecastle.com> et al. (see COPYRIGHT)
8  * William Grzybowski <william@agencialivre.com.br>
9  */
10 
11 #include <config.h>
12 
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <stdarg.h>
16 #include <string.h>
17 #include <syslog.h>
18 #include <ctype.h>
19 #include <time.h>
20 #include <sys/time.h>
21 #include <sys/types.h>
22 #define _XOPEN_SOURCE
23 #include <unistd.h>
24 #include <netdb.h>
25 #include <libpq-fe.h>
26 #include <security/pam_appl.h>
27 
28 #include "backend_pgsql.h"
29 #include "pam_pgsql.h"
30 #include "pam_pgsql_options.h"
31 
32 #if SUPPORT_ATTRIBUTE_VISIBILITY_DEFAULT
33 # define PAM_VISIBLE PAM_EXTERN __attribute__((visibility("default")))
34 #else
35 # define PAM_VISIBLE PAM_EXTERN
36 #endif
37 
38 /* public: authenticate user */
39 PAM_VISIBLE int
40 pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
41 {
42 	modopt_t *options = NULL;
43 	const char *user, *password, *rhost;
44 	int rc;
45 	PGresult *res;
46 	PGconn *conn;
47 
48 	user = NULL; password = NULL; rhost = NULL;
49 
50 	if ((rc = pam_get_item(pamh, PAM_RHOST, (const void **)&rhost)) == PAM_SUCCESS) {
51 
52 		if ((rc = pam_get_user(pamh, &user, NULL)) == PAM_SUCCESS) {
53 
54 			if ((options = mod_options(argc, argv)) != NULL) {
55 
56 				DBGLOG("attempting to authenticate: %s, %s", user, options->query_auth);
57 				if ((rc = pam_get_pass(pamh, PAM_AUTHTOK, &password, PASSWORD_PROMPT, options->std_flags)) == PAM_SUCCESS) {
58 
59 					if ((rc = backend_authenticate(pam_get_service(pamh), user, password, rhost, options)) == PAM_SUCCESS) {
60 						if ((password == 0 || *password == 0) && (flags & PAM_DISALLOW_NULL_AUTHTOK)) {
61 							rc = PAM_AUTH_ERR;
62 						} else {
63 							SYSLOG("(%s) user %s authenticated.", pam_get_service(pamh), user);
64 						}
65 
66 					} else {
67 
68                         char* rhost = NULL;
69                         if (pam_get_item(pamh, PAM_RHOST, (void *) &rhost) == PAM_SUCCESS) {
70                             SYSLOG("couldn't authenticate user %s (%s)", user, rhost);
71                         } else {
72                             SYSLOG("couldn't authenticate user %s", user);
73                         }
74 
75 					}
76 
77 				} else {
78 
79 					SYSLOG("couldn't get pass");
80 
81 				}
82 			}
83 		}
84 	}
85 
86 	if (rc == PAM_SUCCESS) {
87 		if (options->query_auth_succ) {
88 			if ((conn = db_connect(options))) {
89 				pg_execParam(conn, &res, options->query_auth_succ, pam_get_service(pamh), user, password, rhost);
90 				PQclear(res);
91 				PQfinish(conn);
92 			}
93 		}
94 	} else {
95 		if (options->query_auth_fail) {
96 			if ((conn = db_connect(options))) {
97 				pg_execParam(conn, &res, options->query_auth_fail, pam_get_service(pamh), user, password, rhost);
98 				PQclear(res);
99 				PQfinish(conn);
100 			}
101 		}
102 	}
103 
104 	//free_mod_options(options);
105 	return rc;
106 }
107 
108 /* public: check if account has expired, or needs new password */
109 PAM_VISIBLE int
110 pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc,
111                             const char **argv)
112 {
113 	modopt_t *options = NULL;
114 	const char *user, *rhost;
115 	int rc = PAM_AUTH_ERR;
116 	PGconn *conn;
117 	PGresult *res;
118 
119 	user = NULL; rhost = NULL;
120 
121 	if ((options = mod_options(argc, argv)) != NULL) {
122 
123 		/* query not specified, just succeed. */
124 		if (options->query_acct == NULL) {
125 			//free_module_options(options);
126 			return PAM_SUCCESS;
127 		}
128 
129 		if ((rc = pam_get_item(pamh, PAM_RHOST, (const void **)&rhost)) == PAM_SUCCESS) {
130 			if((rc = pam_get_user(pamh, &user, NULL)) == PAM_SUCCESS) {
131 				if(!(conn = db_connect(options))) {
132 					rc = PAM_AUTH_ERR;
133 				} else {
134 					DBGLOG("query: %s", options->query_acct);
135 					rc = PAM_AUTH_ERR;
136 					if(pg_execParam(conn, &res, options->query_acct, pam_get_service(pamh), user, NULL, rhost) == PAM_SUCCESS) {
137 						if (PQntuples(res) == 1 &&
138 						    PQnfields(res) >= 2 && PQnfields(res) <= 3) {
139 							char *expired_db = PQgetvalue(res, 0, 0);
140 							char *newtok_db = PQgetvalue(res, 0, 1);
141 							rc = PAM_SUCCESS;
142 							if (PQnfields(res)>=3) {
143 								char *nulltok_db = PQgetvalue(res, 0, 2);
144 								if ((!strcmp(nulltok_db, "t")) && (flags & PAM_DISALLOW_NULL_AUTHTOK))
145 									rc = PAM_NEW_AUTHTOK_REQD;
146 							}
147 							if (!strcmp(newtok_db, "t"))
148 								rc = PAM_NEW_AUTHTOK_REQD;
149 							if (!strcmp(expired_db, "t"))
150 								rc = PAM_ACCT_EXPIRED;
151 						} else {
152 							DBGLOG("query_acct should return one row and two or three columns");
153 							rc = PAM_PERM_DENIED;
154 						}
155 						PQclear(res);
156 					}
157 					PQfinish(conn);
158 				}
159 			}
160 		}
161 	}
162 
163 	//free_module_options(options);
164 	return rc;
165 }
166 
167 /* public: change password */
168 PAM_VISIBLE int
169 pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
170 {
171 	modopt_t *options = NULL;
172 	int rc;
173 	const char *user, *pass, *newpass, *rhost;
174 	const void *oldtok;
175 	char *newpass_crypt;
176 	PGconn *conn;
177 	PGresult *res;
178 
179 	user = NULL; pass = NULL; newpass = NULL; rhost = NULL; newpass_crypt = NULL;
180 
181 	if ((options = mod_options(argc, argv)) != NULL) {
182 		if ((rc = pam_get_item(pamh, PAM_RHOST, (const void **)&rhost)) == PAM_SUCCESS)
183 			rc = pam_get_user(pamh, &user, NULL);
184 	} else
185 		rc = 1;
186 
187 	if ((rc == PAM_SUCCESS) && (flags & PAM_PRELIM_CHECK)) {
188 		if (getuid() != 0) {
189 			if ((rc = pam_get_pass(pamh, PAM_OLDAUTHTOK, &pass, PASSWORD_PROMPT, options->std_flags)) == PAM_SUCCESS) {
190 				rc = backend_authenticate(pam_get_service(pamh), user, pass, rhost, options);
191 			} else {
192 				SYSLOG("could not retrieve password from '%s'", user);
193 			}
194 		} else {
195 			rc = PAM_SUCCESS;
196 		}
197 	} else if ((rc == PAM_SUCCESS) && (flags & PAM_UPDATE_AUTHTOK)) {
198 
199 		/* only try to check old password if user is not root */
200 		pass = newpass = NULL;
201 		if (getuid() != 0) {
202 
203 			if ((rc = pam_get_item(pamh, PAM_OLDAUTHTOK, &oldtok)) == PAM_SUCCESS) {
204 				pass = (const char*) oldtok;
205 				if ((rc = backend_authenticate(pam_get_service(pamh), user, pass, rhost, options)) != PAM_SUCCESS) {
206 					SYSLOG("(%s) user '%s' not authenticated.", pam_get_service(pamh), user);
207 				}
208 			} else {
209 				SYSLOG("could not retrieve old token");
210 			}
211 
212 		} else {
213 			rc = PAM_SUCCESS;
214 		}
215 
216 		if (rc == PAM_SUCCESS) {
217 
218 			if ((rc = pam_get_confirm_pass(pamh, &newpass, PASSWORD_PROMPT_NEW, PASSWORD_PROMPT_CONFIRM, options->std_flags)) == PAM_SUCCESS) {
219 				if((newpass_crypt = password_encrypt(options, user, newpass, NULL))) {
220 					if(!(conn = db_connect(options))) {
221 						rc = PAM_AUTHINFO_UNAVAIL;
222 					}
223 					if (rc == PAM_SUCCESS) {
224 						DBGLOG("query: %s", options->query_pwd);
225 						if(pg_execParam(conn, &res, options->query_pwd, pam_get_service(pamh), user, newpass_crypt, rhost) != PAM_SUCCESS) {
226 							rc = PAM_AUTH_ERR;
227 						} else {
228 							SYSLOG("(%s) password for '%s' was changed.", pam_get_service(pamh), user);
229 							PQclear(res);
230 						}
231 						PQfinish(conn);
232 					}
233 					free (newpass_crypt);
234 				} else {
235 					rc = PAM_BUF_ERR;
236 				}
237 			} else {
238 				SYSLOG("could not retrieve new authentication tokens");
239 			}
240 		}
241 	}
242 	//free_module_options(options);
243 	if (flags & (PAM_PRELIM_CHECK | PAM_UPDATE_AUTHTOK))
244 		return rc;
245 	else
246 		return PAM_AUTH_ERR;
247 
248 }
249 
250 /* public: just succeed. */
251 PAM_VISIBLE int
252 pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
253 {
254 	return PAM_SUCCESS;
255 }
256 
257 PAM_VISIBLE int
258 pam_sm_open_session(pam_handle_t *pamh, int flags,
259             int argc, const char **argv)
260 {
261 	modopt_t *options = NULL;
262 	const char *user, *rhost;
263 	int rc;
264 	PGresult *res;
265 	PGconn *conn;
266 
267 	user = NULL; rhost = NULL;
268 
269 	if ((options = mod_options(argc, argv)) != NULL) {
270 
271 		if (options->query_session_open) {
272 
273 			if ((rc = pam_get_item(pamh, PAM_RHOST, (const void **)&rhost)) == PAM_SUCCESS) {
274 
275 				if ((rc = pam_get_user(pamh, &user, NULL)) == PAM_SUCCESS) {
276 					DBGLOG("Session opened for user: %s", user);
277 					if ((conn = db_connect(options))) {
278 						pg_execParam(conn, &res, options->query_session_open, pam_get_service(pamh), user, NULL, rhost);
279 						PQclear(res);
280 						PQfinish(conn);
281 					}
282 				}
283 			}
284 		}
285 	///free_module_options(options);
286 	}
287 
288 	return (PAM_SUCCESS);
289 
290 }
291 
292 PAM_VISIBLE int
293 pam_sm_close_session(pam_handle_t *pamh, int flags,
294             int argc, const char *argv[])
295 {
296 	modopt_t *options = NULL;
297 	const char *user, *rhost;
298 	int rc;
299 	PGresult *res;
300 	PGconn *conn;
301 
302 	user = NULL; rhost = NULL;
303 
304 	if ((options = mod_options(argc, argv)) != NULL) {
305 
306 		if (options->query_session_close) {
307 
308 			if ((rc = pam_get_item(pamh, PAM_RHOST, (const void **)&rhost)) == PAM_SUCCESS) {
309 
310 				if ((rc = pam_get_user(pamh, &user, NULL)) == PAM_SUCCESS) {
311 					DBGLOG("Session opened for user: %s", user);
312 					if ((conn = db_connect(options))) {
313                           pg_execParam(conn, &res, options->query_session_close, pam_get_service(pamh), user, NULL, rhost);
314                           PQclear(res);
315                           PQfinish(conn);
316 					}
317 				}
318 			}
319 		}
320 	//free_module_options(options);
321 	}
322 
323 	return (PAM_SUCCESS);
324 
325 }
326