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
42 struct UserDataS {
43 char* user;
44 char* passwd;
45 };
46
47 /* PAM conversation function - for now we only handle one message */
48 int
pamConvFunc(int num_msg,const struct pam_message ** msg,struct pam_response ** respp,void * appdata_ptr)49 pamConvFunc(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. */
svr_auth_pam()169 void 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
247 cleanup:
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