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