summaryrefslogtreecommitdiff
path: root/auth-pam.c
diff options
context:
space:
mode:
Diffstat (limited to 'auth-pam.c')
-rw-r--r--auth-pam.c194
1 files changed, 177 insertions, 17 deletions
diff --git a/auth-pam.c b/auth-pam.c
index 9be57dacd..147f4f8bb 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"
@@ -170,6 +186,7 @@ static int sshpam_account_status = -1;
170static char **sshpam_env = NULL; 186static char **sshpam_env = NULL;
171static Authctxt *sshpam_authctxt = NULL; 187static Authctxt *sshpam_authctxt = NULL;
172static char badpw[] = "\b\n\r\177INCORRECT"; 188static char badpw[] = "\b\n\r\177INCORRECT";
189static const char *sshpam_password = NULL;
173 190
174/* Some PAM implementations don't implement this */ 191/* Some PAM implementations don't implement this */
175#ifndef HAVE_PAM_GETENVLIST 192#ifndef HAVE_PAM_GETENVLIST
@@ -185,8 +202,33 @@ pam_getenvlist(pam_handle_t *pamh)
185} 202}
186#endif 203#endif
187 204
205/*
206 * Some platforms, notably Solaris, do not enforce password complexity
207 * rules during pam_chauthtok() if the real uid of the calling process
208 * is 0, on the assumption that it's being called by "passwd" run by root.
209 * This wraps pam_chauthtok and sets/restore the real uid so PAM will do
210 * the right thing.
211 */
212#ifdef SSHPAM_CHAUTHTOK_NEEDS_RUID
213static int
214sshpam_chauthtok_ruid(pam_handle_t *pamh, int flags)
215{
216 int result;
217
218 if (sshpam_authctxt == NULL)
219 fatal("PAM: sshpam_authctxt not initialized");
220 if (setreuid(sshpam_authctxt->pw->pw_uid, -1) == -1)
221 fatal("%s: setreuid failed: %s", __func__, strerror(errno));
222 result = pam_chauthtok(pamh, flags);
223 if (setreuid(0, -1) == -1)
224 fatal("%s: setreuid failed: %s", __func__, strerror(errno));
225 return result;
226}
227# define pam_chauthtok(a,b) (sshpam_chauthtok_ruid((a), (b)))
228#endif
229
188void 230void
189pam_password_change_required(int reqd) 231sshpam_password_change_required(int reqd)
190{ 232{
191 debug3("%s %d", __func__, reqd); 233 debug3("%s %d", __func__, reqd);
192 if (sshpam_authctxt == NULL) 234 if (sshpam_authctxt == NULL)
@@ -216,7 +258,7 @@ import_environments(Buffer *b)
216#ifndef USE_POSIX_THREADS 258#ifndef USE_POSIX_THREADS
217 /* Import variables set by do_pam_account */ 259 /* Import variables set by do_pam_account */
218 sshpam_account_status = buffer_get_int(b); 260 sshpam_account_status = buffer_get_int(b);
219 pam_password_change_required(buffer_get_int(b)); 261 sshpam_password_change_required(buffer_get_int(b));
220 262
221 /* Import environment from subprocess */ 263 /* Import environment from subprocess */
222 num_env = buffer_get_int(b); 264 num_env = buffer_get_int(b);
@@ -248,7 +290,7 @@ import_environments(Buffer *b)
248 * Conversation function for authentication thread. 290 * Conversation function for authentication thread.
249 */ 291 */
250static int 292static int
251sshpam_thread_conv(int n, const struct pam_message **msg, 293sshpam_thread_conv(int n, struct pam_message **msg,
252 struct pam_response **resp, void *data) 294 struct pam_response **resp, void *data)
253{ 295{
254 Buffer buffer; 296 Buffer buffer;
@@ -259,6 +301,10 @@ sshpam_thread_conv(int n, const struct pam_message **msg,
259 debug3("PAM: %s entering, %d messages", __func__, n); 301 debug3("PAM: %s entering, %d messages", __func__, n);
260 *resp = NULL; 302 *resp = NULL;
261 303
304 if (data == NULL) {
305 error("PAM: conversation function passed a null context");
306 return (PAM_CONV_ERR);
307 }
262 ctxt = data; 308 ctxt = data;
263 if (n <= 0 || n > PAM_MAX_NUM_MSG) 309 if (n <= 0 || n > PAM_MAX_NUM_MSG)
264 return (PAM_CONV_ERR); 310 return (PAM_CONV_ERR);
@@ -336,15 +382,21 @@ sshpam_thread(void *ctxtp)
336 struct pam_ctxt *ctxt = ctxtp; 382 struct pam_ctxt *ctxt = ctxtp;
337 Buffer buffer; 383 Buffer buffer;
338 struct pam_conv sshpam_conv; 384 struct pam_conv sshpam_conv;
385 int flags = (options.permit_empty_passwd == 0 ?
386 PAM_DISALLOW_NULL_AUTHTOK : 0);
339#ifndef USE_POSIX_THREADS 387#ifndef USE_POSIX_THREADS
340 extern char **environ; 388 extern char **environ;
341 char **env_from_pam; 389 char **env_from_pam;
342 u_int i; 390 u_int i;
343 const char *pam_user; 391 const char *pam_user;
344 392
345 pam_get_item(sshpam_handle, PAM_USER, (const void **)&pam_user); 393 pam_get_item(sshpam_handle, PAM_USER, (void **)&pam_user);
346 setproctitle("%s [pam]", pam_user);
347 environ[0] = NULL; 394 environ[0] = NULL;
395
396 if (sshpam_authctxt != NULL) {
397 setproctitle("%s [pam]",
398 sshpam_authctxt->valid ? pam_user : "unknown");
399 }
348#endif 400#endif
349 401
350 sshpam_conv.conv = sshpam_thread_conv; 402 sshpam_conv.conv = sshpam_thread_conv;
@@ -358,7 +410,7 @@ sshpam_thread(void *ctxtp)
358 (const void *)&sshpam_conv); 410 (const void *)&sshpam_conv);
359 if (sshpam_err != PAM_SUCCESS) 411 if (sshpam_err != PAM_SUCCESS)
360 goto auth_fail; 412 goto auth_fail;
361 sshpam_err = pam_authenticate(sshpam_handle, 0); 413 sshpam_err = pam_authenticate(sshpam_handle, flags);
362 if (sshpam_err != PAM_SUCCESS) 414 if (sshpam_err != PAM_SUCCESS)
363 goto auth_fail; 415 goto auth_fail;
364 416
@@ -370,7 +422,7 @@ sshpam_thread(void *ctxtp)
370 PAM_CHANGE_EXPIRED_AUTHTOK); 422 PAM_CHANGE_EXPIRED_AUTHTOK);
371 if (sshpam_err != PAM_SUCCESS) 423 if (sshpam_err != PAM_SUCCESS)
372 goto auth_fail; 424 goto auth_fail;
373 pam_password_change_required(0); 425 sshpam_password_change_required(0);
374 } 426 }
375 } 427 }
376 428
@@ -430,7 +482,7 @@ sshpam_thread_cleanup(void)
430} 482}
431 483
432static int 484static int
433sshpam_null_conv(int n, const struct pam_message **msg, 485sshpam_null_conv(int n, struct pam_message **msg,
434 struct pam_response **resp, void *data) 486 struct pam_response **resp, void *data)
435{ 487{
436 debug3("PAM: %s entering, %d messages", __func__, n); 488 debug3("PAM: %s entering, %d messages", __func__, n);
@@ -468,7 +520,7 @@ sshpam_init(Authctxt *authctxt)
468 if (sshpam_handle != NULL) { 520 if (sshpam_handle != NULL) {
469 /* We already have a PAM context; check if the user matches */ 521 /* We already have a PAM context; check if the user matches */
470 sshpam_err = pam_get_item(sshpam_handle, 522 sshpam_err = pam_get_item(sshpam_handle,
471 PAM_USER, (const void **)&pam_user); 523 PAM_USER, (void **)&pam_user);
472 if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0) 524 if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0)
473 return (0); 525 return (0);
474 pam_end(sshpam_handle, sshpam_err); 526 pam_end(sshpam_handle, sshpam_err);
@@ -725,7 +777,7 @@ do_pam_account(void)
725 } 777 }
726 778
727 if (sshpam_err == PAM_NEW_AUTHTOK_REQD) 779 if (sshpam_err == PAM_NEW_AUTHTOK_REQD)
728 pam_password_change_required(1); 780 sshpam_password_change_required(1);
729 781
730 sshpam_account_status = 1; 782 sshpam_account_status = 1;
731 return (sshpam_account_status); 783 return (sshpam_account_status);
@@ -771,7 +823,7 @@ do_pam_setcred(int init)
771} 823}
772 824
773static int 825static int
774pam_tty_conv(int n, const struct pam_message **msg, 826sshpam_tty_conv(int n, struct pam_message **msg,
775 struct pam_response **resp, void *data) 827 struct pam_response **resp, void *data)
776{ 828{
777 char input[PAM_MAX_MSG_SIZE]; 829 char input[PAM_MAX_MSG_SIZE];
@@ -800,7 +852,8 @@ pam_tty_conv(int n, const struct pam_message **msg,
800 case PAM_PROMPT_ECHO_ON: 852 case PAM_PROMPT_ECHO_ON:
801 fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg)); 853 fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg));
802 fgets(input, sizeof input, stdin); 854 fgets(input, sizeof input, stdin);
803 reply[i].resp = xstrdup(input); 855 if ((reply[i].resp = strdup(input)) == NULL)
856 goto fail;
804 reply[i].resp_retcode = PAM_SUCCESS; 857 reply[i].resp_retcode = PAM_SUCCESS;
805 break; 858 break;
806 case PAM_ERROR_MSG: 859 case PAM_ERROR_MSG:
@@ -824,7 +877,7 @@ pam_tty_conv(int n, const struct pam_message **msg,
824 return (PAM_CONV_ERR); 877 return (PAM_CONV_ERR);
825} 878}
826 879
827static struct pam_conv tty_conv = { pam_tty_conv, NULL }; 880static struct pam_conv tty_conv = { sshpam_tty_conv, NULL };
828 881
829/* 882/*
830 * XXX this should be done in the authentication phase, but ssh1 doesn't 883 * XXX this should be done in the authentication phase, but ssh1 doesn't
@@ -848,7 +901,7 @@ do_pam_chauthtok(void)
848} 901}
849 902
850static int 903static int
851pam_store_conv(int n, const struct pam_message **msg, 904sshpam_store_conv(int n, struct pam_message **msg,
852 struct pam_response **resp, void *data) 905 struct pam_response **resp, void *data)
853{ 906{
854 struct pam_response *reply; 907 struct pam_response *reply;
@@ -890,7 +943,7 @@ pam_store_conv(int n, const struct pam_message **msg,
890 return (PAM_CONV_ERR); 943 return (PAM_CONV_ERR);
891} 944}
892 945
893static struct pam_conv store_conv = { pam_store_conv, NULL }; 946static struct pam_conv store_conv = { sshpam_store_conv, NULL };
894 947
895void 948void
896do_pam_session(void) 949do_pam_session(void)
@@ -957,4 +1010,111 @@ free_pam_environment(char **env)
957 xfree(env); 1010 xfree(env);
958} 1011}
959 1012
1013/*
1014 * "Blind" conversation function for password authentication. Assumes that
1015 * echo-off prompts are for the password and stores messages for later
1016 * display.
1017 */
1018static int
1019sshpam_passwd_conv(int n, struct pam_message **msg,
1020 struct pam_response **resp, void *data)
1021{
1022 struct pam_response *reply;
1023 int i;
1024 size_t len;
1025
1026 debug3("PAM: %s called with %d messages", __func__, n);
1027
1028 *resp = NULL;
1029
1030 if (n <= 0 || n > PAM_MAX_NUM_MSG)
1031 return (PAM_CONV_ERR);
1032
1033 if ((reply = malloc(n * sizeof(*reply))) == NULL)
1034 return (PAM_CONV_ERR);
1035 memset(reply, 0, n * sizeof(*reply));
1036
1037 for (i = 0; i < n; ++i) {
1038 switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
1039 case PAM_PROMPT_ECHO_OFF:
1040 if (sshpam_password == NULL)
1041 goto fail;
1042 if ((reply[i].resp = strdup(sshpam_password)) == NULL)
1043 goto fail;
1044 reply[i].resp_retcode = PAM_SUCCESS;
1045 break;
1046 case PAM_ERROR_MSG:
1047 case PAM_TEXT_INFO:
1048 len = strlen(PAM_MSG_MEMBER(msg, i, msg));
1049 if (len > 0) {
1050 buffer_append(&loginmsg,
1051 PAM_MSG_MEMBER(msg, i, msg), len);
1052 buffer_append(&loginmsg, "\n", 1);
1053 }
1054 if ((reply[i].resp = strdup("")) == NULL)
1055 goto fail;
1056 reply[i].resp_retcode = PAM_SUCCESS;
1057 break;
1058 default:
1059 goto fail;
1060 }
1061 }
1062 *resp = reply;
1063 return (PAM_SUCCESS);
1064
1065 fail:
1066 for(i = 0; i < n; i++) {
1067 if (reply[i].resp != NULL)
1068 xfree(reply[i].resp);
1069 }
1070 xfree(reply);
1071 return (PAM_CONV_ERR);
1072}
1073
1074static struct pam_conv passwd_conv = { sshpam_passwd_conv, NULL };
1075
1076/*
1077 * Attempt password authentication via PAM
1078 */
1079int
1080sshpam_auth_passwd(Authctxt *authctxt, const char *password)
1081{
1082 int flags = (options.permit_empty_passwd == 0 ?
1083 PAM_DISALLOW_NULL_AUTHTOK : 0);
1084
1085 if (!options.use_pam || sshpam_handle == NULL)
1086 fatal("PAM: %s called when PAM disabled or failed to "
1087 "initialise.", __func__);
1088
1089 sshpam_password = password;
1090 sshpam_authctxt = authctxt;
1091
1092 /*
1093 * If the user logging in is invalid, or is root but is not permitted
1094 * by PermitRootLogin, use an invalid password to prevent leaking
1095 * information via timing (eg if the PAM config has a delay on fail).
1096 */
1097 if (!authctxt->valid || (authctxt->pw->pw_uid == 0 &&
1098 options.permit_root_login != PERMIT_YES))
1099 sshpam_password = badpw;
1100
1101 sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
1102 (const void *)&passwd_conv);
1103 if (sshpam_err != PAM_SUCCESS)
1104 fatal("PAM: %s: failed to set PAM_CONV: %s", __func__,
1105 pam_strerror(sshpam_handle, sshpam_err));
1106
1107 sshpam_err = pam_authenticate(sshpam_handle, flags);
1108 sshpam_password = NULL;
1109 if (sshpam_err == PAM_SUCCESS && authctxt->valid) {
1110 debug("PAM: password authentication accepted for %.100s",
1111 authctxt->user);
1112 return 1;
1113 } else {
1114 debug("PAM: password authentication failed for %.100s: %s",
1115 authctxt->valid ? authctxt->user : "an illegal user",
1116 pam_strerror(sshpam_handle, sshpam_err));
1117 return 0;
1118 }
1119}
960#endif /* USE_PAM */ 1120#endif /* USE_PAM */