summaryrefslogtreecommitdiff
path: root/auth-pam.c
diff options
context:
space:
mode:
Diffstat (limited to 'auth-pam.c')
-rw-r--r--auth-pam.c206
1 files changed, 187 insertions, 19 deletions
diff --git a/auth-pam.c b/auth-pam.c
index 36dbb7e15..b93241f48 100644
--- a/auth-pam.c
+++ b/auth-pam.c
@@ -28,10 +28,26 @@
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE. 29 * SUCH DAMAGE.
30 */ 30 */
31/*
32 * Copyright (c) 2003,2004 Damien Miller <djm@mindrot.org>
33 * Copyright (c) 2003,2004 Darren Tucker <dtucker@zip.com.au>
34 *
35 * Permission to use, copy, modify, and distribute this software for any
36 * purpose with or without fee is hereby granted, provided that the above
37 * copyright notice and this permission notice appear in all copies.
38 *
39 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
40 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
41 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
42 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
43 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
44 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
45 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
46 */
31 47
32/* Based on $FreeBSD: src/crypto/openssh/auth2-pam-freebsd.c,v 1.11 2003/03/31 13:48:18 des Exp $ */ 48/* Based on $FreeBSD: src/crypto/openssh/auth2-pam-freebsd.c,v 1.11 2003/03/31 13:48:18 des Exp $ */
33#include "includes.h" 49#include "includes.h"
34RCSID("$Id: auth-pam.c,v 1.100 2004/04/18 01:00:26 dtucker Exp $"); 50RCSID("$Id: auth-pam.c,v 1.114 2004/08/16 13:12:06 dtucker Exp $");
35 51
36#ifdef USE_PAM 52#ifdef USE_PAM
37#if defined(HAVE_SECURITY_PAM_APPL_H) 53#if defined(HAVE_SECURITY_PAM_APPL_H)
@@ -49,7 +65,7 @@ RCSID("$Id: auth-pam.c,v 1.100 2004/04/18 01:00:26 dtucker Exp $");
49#include "monitor_wrap.h" 65#include "monitor_wrap.h"
50#include "msg.h" 66#include "msg.h"
51#include "packet.h" 67#include "packet.h"
52#include "readpass.h" 68#include "misc.h"
53#include "servconf.h" 69#include "servconf.h"
54#include "ssh2.h" 70#include "ssh2.h"
55#include "xmalloc.h" 71#include "xmalloc.h"
@@ -93,10 +109,17 @@ static mysig_t sshpam_oldsig;
93static void 109static void
94sshpam_sigchld_handler(int sig) 110sshpam_sigchld_handler(int sig)
95{ 111{
112 signal(SIGCHLD, SIG_DFL);
96 if (cleanup_ctxt == NULL) 113 if (cleanup_ctxt == NULL)
97 return; /* handler called after PAM cleanup, shouldn't happen */ 114 return; /* handler called after PAM cleanup, shouldn't happen */
98 if (waitpid(cleanup_ctxt->pam_thread, &sshpam_thread_status, 0) == -1) 115 if (waitpid(cleanup_ctxt->pam_thread, &sshpam_thread_status, WNOHANG)
99 return; /* couldn't wait for process */ 116 <= 0) {
117 /* PAM thread has not exitted, privsep slave must have */
118 kill(cleanup_ctxt->pam_thread, SIGTERM);
119 if (waitpid(cleanup_ctxt->pam_thread, &sshpam_thread_status, 0)
120 <= 0)
121 return; /* could not wait */
122 }
100 if (WIFSIGNALED(sshpam_thread_status) && 123 if (WIFSIGNALED(sshpam_thread_status) &&
101 WTERMSIG(sshpam_thread_status) == SIGTERM) 124 WTERMSIG(sshpam_thread_status) == SIGTERM)
102 return; /* terminated by pthread_cancel */ 125 return; /* terminated by pthread_cancel */
@@ -162,6 +185,7 @@ static int sshpam_cred_established = 0;
162static int sshpam_account_status = -1; 185static int sshpam_account_status = -1;
163static char **sshpam_env = NULL; 186static char **sshpam_env = NULL;
164static Authctxt *sshpam_authctxt = NULL; 187static Authctxt *sshpam_authctxt = NULL;
188static const char *sshpam_password = NULL;
165 189
166/* Some PAM implementations don't implement this */ 190/* Some PAM implementations don't implement this */
167#ifndef HAVE_PAM_GETENVLIST 191#ifndef HAVE_PAM_GETENVLIST
@@ -177,8 +201,33 @@ pam_getenvlist(pam_handle_t *pamh)
177} 201}
178#endif 202#endif
179 203
204/*
205 * Some platforms, notably Solaris, do not enforce password complexity
206 * rules during pam_chauthtok() if the real uid of the calling process
207 * is 0, on the assumption that it's being called by "passwd" run by root.
208 * This wraps pam_chauthtok and sets/restore the real uid so PAM will do
209 * the right thing.
210 */
211#ifdef SSHPAM_CHAUTHTOK_NEEDS_RUID
212static int
213sshpam_chauthtok_ruid(pam_handle_t *pamh, int flags)
214{
215 int result;
216
217 if (sshpam_authctxt == NULL)
218 fatal("PAM: sshpam_authctxt not initialized");
219 if (setreuid(sshpam_authctxt->pw->pw_uid, -1) == -1)
220 fatal("%s: setreuid failed: %s", __func__, strerror(errno));
221 result = pam_chauthtok(pamh, flags);
222 if (setreuid(0, -1) == -1)
223 fatal("%s: setreuid failed: %s", __func__, strerror(errno));
224 return result;
225}
226# define pam_chauthtok(a,b) (sshpam_chauthtok_ruid((a), (b)))
227#endif
228
180void 229void
181pam_password_change_required(int reqd) 230sshpam_password_change_required(int reqd)
182{ 231{
183 debug3("%s %d", __func__, reqd); 232 debug3("%s %d", __func__, reqd);
184 if (sshpam_authctxt == NULL) 233 if (sshpam_authctxt == NULL)
@@ -208,7 +257,7 @@ import_environments(Buffer *b)
208#ifndef USE_POSIX_THREADS 257#ifndef USE_POSIX_THREADS
209 /* Import variables set by do_pam_account */ 258 /* Import variables set by do_pam_account */
210 sshpam_account_status = buffer_get_int(b); 259 sshpam_account_status = buffer_get_int(b);
211 pam_password_change_required(buffer_get_int(b)); 260 sshpam_password_change_required(buffer_get_int(b));
212 261
213 /* Import environment from subprocess */ 262 /* Import environment from subprocess */
214 num_env = buffer_get_int(b); 263 num_env = buffer_get_int(b);
@@ -240,7 +289,7 @@ import_environments(Buffer *b)
240 * Conversation function for authentication thread. 289 * Conversation function for authentication thread.
241 */ 290 */
242static int 291static int
243sshpam_thread_conv(int n, const struct pam_message **msg, 292sshpam_thread_conv(int n, struct pam_message **msg,
244 struct pam_response **resp, void *data) 293 struct pam_response **resp, void *data)
245{ 294{
246 Buffer buffer; 295 Buffer buffer;
@@ -251,6 +300,10 @@ sshpam_thread_conv(int n, const struct pam_message **msg,
251 debug3("PAM: %s entering, %d messages", __func__, n); 300 debug3("PAM: %s entering, %d messages", __func__, n);
252 *resp = NULL; 301 *resp = NULL;
253 302
303 if (data == NULL) {
304 error("PAM: conversation function passed a null context");
305 return (PAM_CONV_ERR);
306 }
254 ctxt = data; 307 ctxt = data;
255 if (n <= 0 || n > PAM_MAX_NUM_MSG) 308 if (n <= 0 || n > PAM_MAX_NUM_MSG)
256 return (PAM_CONV_ERR); 309 return (PAM_CONV_ERR);
@@ -328,15 +381,21 @@ sshpam_thread(void *ctxtp)
328 struct pam_ctxt *ctxt = ctxtp; 381 struct pam_ctxt *ctxt = ctxtp;
329 Buffer buffer; 382 Buffer buffer;
330 struct pam_conv sshpam_conv; 383 struct pam_conv sshpam_conv;
384 int flags = (options.permit_empty_passwd == 0 ?
385 PAM_DISALLOW_NULL_AUTHTOK : 0);
331#ifndef USE_POSIX_THREADS 386#ifndef USE_POSIX_THREADS
332 extern char **environ; 387 extern char **environ;
333 char **env_from_pam; 388 char **env_from_pam;
334 u_int i; 389 u_int i;
335 const char *pam_user; 390 const char *pam_user;
336 391
337 pam_get_item(sshpam_handle, PAM_USER, (const void **)&pam_user); 392 pam_get_item(sshpam_handle, PAM_USER, (void **)&pam_user);
338 setproctitle("%s [pam]", pam_user);
339 environ[0] = NULL; 393 environ[0] = NULL;
394
395 if (sshpam_authctxt != NULL) {
396 setproctitle("%s [pam]",
397 sshpam_authctxt->valid ? pam_user : "unknown");
398 }
340#endif 399#endif
341 400
342 sshpam_conv.conv = sshpam_thread_conv; 401 sshpam_conv.conv = sshpam_thread_conv;
@@ -350,7 +409,7 @@ sshpam_thread(void *ctxtp)
350 (const void *)&sshpam_conv); 409 (const void *)&sshpam_conv);
351 if (sshpam_err != PAM_SUCCESS) 410 if (sshpam_err != PAM_SUCCESS)
352 goto auth_fail; 411 goto auth_fail;
353 sshpam_err = pam_authenticate(sshpam_handle, 0); 412 sshpam_err = pam_authenticate(sshpam_handle, flags);
354 if (sshpam_err != PAM_SUCCESS) 413 if (sshpam_err != PAM_SUCCESS)
355 goto auth_fail; 414 goto auth_fail;
356 415
@@ -362,7 +421,7 @@ sshpam_thread(void *ctxtp)
362 PAM_CHANGE_EXPIRED_AUTHTOK); 421 PAM_CHANGE_EXPIRED_AUTHTOK);
363 if (sshpam_err != PAM_SUCCESS) 422 if (sshpam_err != PAM_SUCCESS)
364 goto auth_fail; 423 goto auth_fail;
365 pam_password_change_required(0); 424 sshpam_password_change_required(0);
366 } 425 }
367 } 426 }
368 427
@@ -422,7 +481,7 @@ sshpam_thread_cleanup(void)
422} 481}
423 482
424static int 483static int
425sshpam_null_conv(int n, const struct pam_message **msg, 484sshpam_null_conv(int n, struct pam_message **msg,
426 struct pam_response **resp, void *data) 485 struct pam_response **resp, void *data)
427{ 486{
428 debug3("PAM: %s entering, %d messages", __func__, n); 487 debug3("PAM: %s entering, %d messages", __func__, n);
@@ -460,7 +519,7 @@ sshpam_init(Authctxt *authctxt)
460 if (sshpam_handle != NULL) { 519 if (sshpam_handle != NULL) {
461 /* We already have a PAM context; check if the user matches */ 520 /* We already have a PAM context; check if the user matches */
462 sshpam_err = pam_get_item(sshpam_handle, 521 sshpam_err = pam_get_item(sshpam_handle,
463 PAM_USER, (const void **)&pam_user); 522 PAM_USER, (void **)&pam_user);
464 if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0) 523 if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0)
465 return (0); 524 return (0);
466 pam_end(sshpam_handle, sshpam_err); 525 pam_end(sshpam_handle, sshpam_err);
@@ -712,7 +771,7 @@ do_pam_account(void)
712 } 771 }
713 772
714 if (sshpam_err == PAM_NEW_AUTHTOK_REQD) 773 if (sshpam_err == PAM_NEW_AUTHTOK_REQD)
715 pam_password_change_required(1); 774 sshpam_password_change_required(1);
716 775
717 sshpam_account_status = 1; 776 sshpam_account_status = 1;
718 return (sshpam_account_status); 777 return (sshpam_account_status);
@@ -758,7 +817,7 @@ do_pam_setcred(int init)
758} 817}
759 818
760static int 819static int
761pam_tty_conv(int n, const struct pam_message **msg, 820sshpam_tty_conv(int n, struct pam_message **msg,
762 struct pam_response **resp, void *data) 821 struct pam_response **resp, void *data)
763{ 822{
764 char input[PAM_MAX_MSG_SIZE]; 823 char input[PAM_MAX_MSG_SIZE];
@@ -787,7 +846,8 @@ pam_tty_conv(int n, const struct pam_message **msg,
787 case PAM_PROMPT_ECHO_ON: 846 case PAM_PROMPT_ECHO_ON:
788 fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg)); 847 fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg));
789 fgets(input, sizeof input, stdin); 848 fgets(input, sizeof input, stdin);
790 reply[i].resp = xstrdup(input); 849 if ((reply[i].resp = strdup(input)) == NULL)
850 goto fail;
791 reply[i].resp_retcode = PAM_SUCCESS; 851 reply[i].resp_retcode = PAM_SUCCESS;
792 break; 852 break;
793 case PAM_ERROR_MSG: 853 case PAM_ERROR_MSG:
@@ -811,7 +871,7 @@ pam_tty_conv(int n, const struct pam_message **msg,
811 return (PAM_CONV_ERR); 871 return (PAM_CONV_ERR);
812} 872}
813 873
814static struct pam_conv tty_conv = { pam_tty_conv, NULL }; 874static struct pam_conv tty_conv = { sshpam_tty_conv, NULL };
815 875
816/* 876/*
817 * XXX this should be done in the authentication phase, but ssh1 doesn't 877 * XXX this should be done in the authentication phase, but ssh1 doesn't
@@ -835,7 +895,7 @@ do_pam_chauthtok(void)
835} 895}
836 896
837static int 897static int
838pam_store_conv(int n, const struct pam_message **msg, 898sshpam_store_conv(int n, struct pam_message **msg,
839 struct pam_response **resp, void *data) 899 struct pam_response **resp, void *data)
840{ 900{
841 struct pam_response *reply; 901 struct pam_response *reply;
@@ -877,7 +937,7 @@ pam_store_conv(int n, const struct pam_message **msg,
877 return (PAM_CONV_ERR); 937 return (PAM_CONV_ERR);
878} 938}
879 939
880static struct pam_conv store_conv = { pam_store_conv, NULL }; 940static struct pam_conv store_conv = { sshpam_store_conv, NULL };
881 941
882void 942void
883do_pam_session(void) 943do_pam_session(void)
@@ -944,4 +1004,112 @@ free_pam_environment(char **env)
944 xfree(env); 1004 xfree(env);
945} 1005}
946 1006
1007/*
1008 * "Blind" conversation function for password authentication. Assumes that
1009 * echo-off prompts are for the password and stores messages for later
1010 * display.
1011 */
1012static int
1013sshpam_passwd_conv(int n, struct pam_message **msg,
1014 struct pam_response **resp, void *data)
1015{
1016 struct pam_response *reply;
1017 int i;
1018 size_t len;
1019
1020 debug3("PAM: %s called with %d messages", __func__, n);
1021
1022 *resp = NULL;
1023
1024 if (n <= 0 || n > PAM_MAX_NUM_MSG)
1025 return (PAM_CONV_ERR);
1026
1027 if ((reply = malloc(n * sizeof(*reply))) == NULL)
1028 return (PAM_CONV_ERR);
1029 memset(reply, 0, n * sizeof(*reply));
1030
1031 for (i = 0; i < n; ++i) {
1032 switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
1033 case PAM_PROMPT_ECHO_OFF:
1034 if (sshpam_password == NULL)
1035 goto fail;
1036 if ((reply[i].resp = strdup(sshpam_password)) == NULL)
1037 goto fail;
1038 reply[i].resp_retcode = PAM_SUCCESS;
1039 break;
1040 case PAM_ERROR_MSG:
1041 case PAM_TEXT_INFO:
1042 len = strlen(PAM_MSG_MEMBER(msg, i, msg));
1043 if (len > 0) {
1044 buffer_append(&loginmsg,
1045 PAM_MSG_MEMBER(msg, i, msg), len);
1046 buffer_append(&loginmsg, "\n", 1);
1047 }
1048 if ((reply[i].resp = strdup("")) == NULL)
1049 goto fail;
1050 reply[i].resp_retcode = PAM_SUCCESS;
1051 break;
1052 default:
1053 goto fail;
1054 }
1055 }
1056 *resp = reply;
1057 return (PAM_SUCCESS);
1058
1059 fail:
1060 for(i = 0; i < n; i++) {
1061 if (reply[i].resp != NULL)
1062 xfree(reply[i].resp);
1063 }
1064 xfree(reply);
1065 return (PAM_CONV_ERR);
1066}
1067
1068static struct pam_conv passwd_conv = { sshpam_passwd_conv, NULL };
1069
1070/*
1071 * Attempt password authentication via PAM
1072 */
1073int
1074sshpam_auth_passwd(Authctxt *authctxt, const char *password)
1075{
1076 int flags = (options.permit_empty_passwd == 0 ?
1077 PAM_DISALLOW_NULL_AUTHTOK : 0);
1078 static char badpw[] = "\b\n\r\177INCORRECT";
1079
1080 if (!options.use_pam || sshpam_handle == NULL)
1081 fatal("PAM: %s called when PAM disabled or failed to "
1082 "initialise.", __func__);
1083
1084 sshpam_password = password;
1085 sshpam_authctxt = authctxt;
1086
1087 /*
1088 * If the user logging in is invalid, or is root but is not permitted
1089 * by PermitRootLogin, use an invalid password to prevent leaking
1090 * information via timing (eg if the PAM config has a delay on fail).
1091 */
1092 if (!authctxt->valid || (authctxt->pw->pw_uid == 0 &&
1093 options.permit_root_login != PERMIT_YES))
1094 sshpam_password = badpw;
1095
1096 sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
1097 (const void *)&passwd_conv);
1098 if (sshpam_err != PAM_SUCCESS)
1099 fatal("PAM: %s: failed to set PAM_CONV: %s", __func__,
1100 pam_strerror(sshpam_handle, sshpam_err));
1101
1102 sshpam_err = pam_authenticate(sshpam_handle, flags);
1103 sshpam_password = NULL;
1104 if (sshpam_err == PAM_SUCCESS && authctxt->valid) {
1105 debug("PAM: password authentication accepted for %.100s",
1106 authctxt->user);
1107 return 1;
1108 } else {
1109 debug("PAM: password authentication failed for %.100s: %s",
1110 authctxt->valid ? authctxt->user : "an illegal user",
1111 pam_strerror(sshpam_handle, sshpam_err));
1112 return 0;
1113 }
1114}
947#endif /* USE_PAM */ 1115#endif /* USE_PAM */