svr-authpam.c revision 29698e296e3fd1474a362fbd229bca888175bb66
1/*
2 * Dropbear SSH
3 *
4 * Copyright (c) 2004 Martin Carlsson
5 * Portions (c) 2004 Matt Johnston
6 * All rights reserved.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 * SOFTWARE. */
25
26/* Validates a user password using PAM */
27
28#include "includes.h"
29#include "session.h"
30#include "buffer.h"
31#include "dbutil.h"
32#include "auth.h"
33
34#ifdef ENABLE_SVR_PAM_AUTH
35
36#if defined(HAVE_SECURITY_PAM_APPL_H)
37#include <security/pam_appl.h>
38#elif defined (HAVE_PAM_PAM_APPL_H)
39#include <pam/pam_appl.h>
40#endif
41
42struct UserDataS {
43	char* user;
44	char* passwd;
45};
46
47/* PAM conversation function - for now we only handle one message */
48int
49pamConvFunc(int num_msg,
50		const struct pam_message **msg,
51		struct pam_response **respp,
52		void *appdata_ptr) {
53
54	int rc = PAM_SUCCESS;
55	struct pam_response* resp = NULL;
56	struct UserDataS* userDatap = (struct UserDataS*) appdata_ptr;
57	unsigned int msg_len = 0;
58	unsigned int i = 0;
59
60	const char* message = (*msg)->msg;
61
62	/* make a copy we can strip */
63	char * compare_message = m_strdup(message);
64
65	TRACE(("enter pamConvFunc"))
66
67	if (num_msg != 1) {
68		/* If you're getting here - Dropbear probably can't support your pam
69		 * modules. This whole file is a bit of a hack around lack of
70		 * asynchronocity in PAM anyway. */
71		dropbear_log(LOG_INFO, "pamConvFunc() called with >1 messages: not supported.");
72		return PAM_CONV_ERR;
73	}
74
75	TRACE(("msg_style is %d", (*msg)->msg_style))
76	if (compare_message) {
77		TRACE(("message is '%s'", compare_message))
78	} else {
79		TRACE(("null message"))
80	}
81
82
83	/* Make the string lowercase. */
84	msg_len = strlen(compare_message);
85	for (i = 0; i < msg_len; i++) {
86		compare_message[i] = tolower(compare_message[i]);
87	}
88
89	/* If the string ends with ": ", remove the space.
90	   ie "login: " vs "login:" */
91	if (msg_len > 2
92			&& compare_message[msg_len-2] == ':'
93			&& compare_message[msg_len-1] == ' ') {
94		compare_message[msg_len-1] = '\0';
95	}
96
97	switch((*msg)->msg_style) {
98
99		case PAM_PROMPT_ECHO_OFF:
100
101			if (!(strcmp(compare_message, "password:") == 0)) {
102				/* We don't recognise the prompt as asking for a password,
103				   so can't handle it. Add more above as required for
104				   different pam modules/implementations */
105				dropbear_log(LOG_NOTICE, "PAM unknown prompt %s (no echo)",
106						compare_message);
107				rc = PAM_CONV_ERR;
108				break;
109			}
110
111			/* You have to read the PAM module-writers' docs (do we look like
112			 * module writers? no.) to find out that the module will
113			 * free the pam_response and its resp element - ie we _must_ malloc
114			 * it here */
115			resp = (struct pam_response*) m_malloc(sizeof(struct pam_response));
116			memset(resp, 0, sizeof(struct pam_response));
117
118			resp->resp = m_strdup(userDatap->passwd);
119			m_burn(userDatap->passwd, strlen(userDatap->passwd));
120			(*respp) = resp;
121			break;
122
123
124		case PAM_PROMPT_ECHO_ON:
125
126			if (!((strcmp(compare_message, "login:" ) == 0)
127				|| (strcmp(compare_message, "please enter username:") == 0))) {
128				/* We don't recognise the prompt as asking for a username,
129				   so can't handle it. Add more above as required for
130				   different pam modules/implementations */
131				dropbear_log(LOG_NOTICE, "PAM unknown prompt %s (with echo)",
132						compare_message);
133				rc = PAM_CONV_ERR;
134				break;
135			}
136
137			/* You have to read the PAM module-writers' docs (do we look like
138			 * module writers? no.) to find out that the module will
139			 * free the pam_response and its resp element - ie we _must_ malloc
140			 * it here */
141			resp = (struct pam_response*) m_malloc(sizeof(struct pam_response));
142			memset(resp, 0, sizeof(struct pam_response));
143
144			resp->resp = m_strdup(userDatap->user);
145			TRACE(("userDatap->user='%s'", userDatap->user))
146			(*respp) = resp;
147			break;
148
149		default:
150			TRACE(("Unknown message type"))
151			rc = PAM_CONV_ERR;
152			break;
153	}
154
155	m_free(compare_message);
156	TRACE(("leave pamConvFunc, rc %d", rc))
157
158	return rc;
159}
160
161/* Process a password auth request, sending success or failure messages as
162 * appropriate. To the client it looks like it's doing normal password auth (as
163 * opposed to keyboard-interactive or something), so the pam module has to be
164 * fairly standard (ie just "what's your username, what's your password, OK").
165 *
166 * Keyboard interactive would be a lot nicer, but since PAM is synchronous, it
167 * gets very messy trying to send the interactive challenges, and read the
168 * interactive responses, over the network. */
169void svr_auth_pam() {
170
171	struct UserDataS userData = {NULL, NULL};
172	struct pam_conv pamConv = {
173		pamConvFunc,
174		&userData /* submitted to pamvConvFunc as appdata_ptr */
175	};
176
177	pam_handle_t* pamHandlep = NULL;
178
179	unsigned char * password = NULL;
180	unsigned int passwordlen;
181
182	int rc = PAM_SUCCESS;
183	unsigned char changepw;
184
185	/* check if client wants to change password */
186	changepw = buf_getbool(ses.payload);
187	if (changepw) {
188		/* not implemented by this server */
189		send_msg_userauth_failure(0, 1);
190		goto cleanup;
191	}
192
193	password = buf_getstring(ses.payload, &passwordlen);
194
195	/* used to pass data to the PAM conversation function - don't bother with
196	 * strdup() etc since these are touched only by our own conversation
197	 * function (above) which takes care of it */
198	userData.user = ses.authstate.printableuser;
199	userData.passwd = password;
200
201	/* Init pam */
202	if ((rc = pam_start("sshd", NULL, &pamConv, &pamHandlep)) != PAM_SUCCESS) {
203		dropbear_log(LOG_WARNING, "pam_start() failed, rc=%d, %s\n",
204				rc, pam_strerror(pamHandlep, rc));
205		goto cleanup;
206	}
207
208	/* just to set it to something */
209	if ((rc = pam_set_item(pamHandlep, PAM_TTY, "ssh") != PAM_SUCCESS)) {
210		dropbear_log(LOG_WARNING, "pam_set_item() failed, rc=%d, %s\n",
211				rc, pam_strerror(pamHandlep, rc));
212		goto cleanup;
213	}
214
215	(void) pam_fail_delay(pamHandlep, 0 /* musec_delay */);
216
217	/* (void) pam_set_item(pamHandlep, PAM_FAIL_DELAY, (void*) pamDelayFunc); */
218
219	if ((rc = pam_authenticate(pamHandlep, 0)) != PAM_SUCCESS) {
220		dropbear_log(LOG_WARNING, "pam_authenticate() failed, rc=%d, %s\n",
221				rc, pam_strerror(pamHandlep, rc));
222		dropbear_log(LOG_WARNING,
223				"bad PAM password attempt for '%s' from %s",
224				ses.authstate.printableuser,
225				svr_ses.addrstring);
226		send_msg_userauth_failure(0, 1);
227		goto cleanup;
228	}
229
230	if ((rc = pam_acct_mgmt(pamHandlep, 0)) != PAM_SUCCESS) {
231		dropbear_log(LOG_WARNING, "pam_acct_mgmt() failed, rc=%d, %s\n",
232				rc, pam_strerror(pamHandlep, rc));
233		dropbear_log(LOG_WARNING,
234				"bad PAM password attempt for '%s' from %s",
235				ses.authstate.printableuser,
236				svr_ses.addrstring);
237		send_msg_userauth_failure(0, 1);
238		goto cleanup;
239	}
240
241	/* successful authentication */
242	dropbear_log(LOG_NOTICE, "PAM password auth succeeded for '%s' from %s",
243			ses.authstate.printableuser,
244			svr_ses.addrstring);
245	send_msg_userauth_success();
246
247cleanup:
248	if (password != NULL) {
249		m_burn(password, passwordlen);
250		m_free(password);
251	}
252	if (pamHandlep != NULL) {
253		TRACE(("pam_end"))
254		(void) pam_end(pamHandlep, 0 /* pam_status */);
255	}
256}
257
258#endif /* ENABLE_SVR_PAM_AUTH */
259