1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
1 |
/*-
|
2 |
* Copyright (c) 2002 Networks Associates Technology, Inc.
|
|
3 |
* All rights reserved.
|
|
4 |
*
|
|
5 |
* This software was developed for the FreeBSD Project by ThinkSec AS and
|
|
6 |
* NAI Labs, the Security Research Division of Network Associates, Inc.
|
|
7 |
* under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
|
|
8 |
* DARPA CHATS research program.
|
|
9 |
*
|
|
10 |
* Redistribution and use in source and binary forms, with or without
|
|
11 |
* modification, are permitted provided that the following conditions
|
|
12 |
* are met:
|
|
13 |
* 1. Redistributions of source code must retain the above copyright
|
|
14 |
* notice, this list of conditions and the following disclaimer.
|
|
15 |
* 2. Redistributions in binary form must reproduce the above copyright
|
|
16 |
* notice, this list of conditions and the following disclaimer in the
|
|
17 |
* documentation and/or other materials provided with the distribution.
|
|
18 |
*
|
|
19 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
20 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
22 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
23 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
25 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
26 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
27 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
28 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
29 |
* SUCH DAMAGE.
|
|
30 |
*/
|
|
1.1.1
by Colin Watson
Import upstream version 3.9p1 |
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 |
*/
|
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
47 |
|
48 |
/* Based on $FreeBSD: src/crypto/openssh/auth2-pam-freebsd.c,v 1.11 2003/03/31 13:48:18 des Exp $ */
|
|
49 |
#include "includes.h" |
|
1.13.1
by Colin Watson
Import upstream version 4.6p1 |
50 |
|
51 |
#include <sys/types.h> |
|
52 |
#include <sys/stat.h> |
|
53 |
#include <sys/wait.h> |
|
54 |
||
55 |
#include <errno.h> |
|
56 |
#include <signal.h> |
|
57 |
#include <stdarg.h> |
|
58 |
#include <string.h> |
|
59 |
#include <unistd.h> |
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
60 |
|
61 |
#ifdef USE_PAM
|
|
62 |
#if defined(HAVE_SECURITY_PAM_APPL_H)
|
|
63 |
#include <security/pam_appl.h> |
|
64 |
#elif defined (HAVE_PAM_PAM_APPL_H)
|
|
65 |
#include <pam/pam_appl.h> |
|
66 |
#endif
|
|
67 |
||
1.1.3
by Colin Watson
Import upstream version 4.2p1 |
68 |
/* OpenGroup RFC86.0 and XSSO specify no "const" on arguments */
|
69 |
#ifdef PAM_SUN_CODEBASE
|
|
70 |
# define sshpam_const /* Solaris, HP-UX, AIX */ |
|
71 |
#else
|
|
72 |
# define sshpam_const const /* LinuxPAM, OpenPAM */ |
|
73 |
#endif
|
|
74 |
||
1.13.1
by Colin Watson
Import upstream version 4.6p1 |
75 |
/* Ambiguity in spec: is it an array of pointers or a pointer to an array? */
|
76 |
#ifdef PAM_SUN_CODEBASE
|
|
77 |
# define PAM_MSG_MEMBER(msg, n, member) ((*(msg))[(n)].member)
|
|
78 |
#else
|
|
79 |
# define PAM_MSG_MEMBER(msg, n, member) ((msg)[(n)]->member)
|
|
80 |
#endif
|
|
81 |
||
82 |
#include "xmalloc.h" |
|
83 |
#include "buffer.h" |
|
84 |
#include "key.h" |
|
85 |
#include "hostfile.h" |
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
86 |
#include "auth.h" |
87 |
#include "auth-pam.h" |
|
88 |
#include "canohost.h" |
|
89 |
#include "log.h" |
|
90 |
#include "msg.h" |
|
91 |
#include "packet.h" |
|
1.1.1
by Colin Watson
Import upstream version 3.9p1 |
92 |
#include "misc.h" |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
93 |
#include "servconf.h" |
94 |
#include "ssh2.h" |
|
95 |
#include "auth-options.h" |
|
1.13.1
by Colin Watson
Import upstream version 4.6p1 |
96 |
#ifdef GSSAPI
|
97 |
#include "ssh-gss.h" |
|
98 |
#endif
|
|
99 |
#include "monitor_wrap.h" |
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
100 |
|
101 |
extern ServerOptions options; |
|
102 |
extern Buffer loginmsg; |
|
103 |
extern int compat20; |
|
104 |
extern u_int utmp_len; |
|
105 |
||
1.1.2
by Colin Watson
Import upstream version 4.1p1 |
106 |
/* so we don't silently change behaviour */
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
107 |
#ifdef USE_POSIX_THREADS
|
1.1.2
by Colin Watson
Import upstream version 4.1p1 |
108 |
# error "USE_POSIX_THREADS replaced by UNSUPPORTED_POSIX_THREADS_HACK"
|
109 |
#endif
|
|
110 |
||
111 |
/*
|
|
112 |
* Formerly known as USE_POSIX_THREADS, using this is completely unsupported
|
|
113 |
* and generally a bad idea. Use at own risk and do not expect support if
|
|
114 |
* this breaks.
|
|
115 |
*/
|
|
116 |
#ifdef UNSUPPORTED_POSIX_THREADS_HACK
|
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
117 |
#include <pthread.h> |
118 |
/*
|
|
119 |
* Avoid namespace clash when *not* using pthreads for systems *with*
|
|
120 |
* pthreads, which unconditionally define pthread_t via sys/types.h
|
|
121 |
* (e.g. Linux)
|
|
122 |
*/
|
|
123 |
typedef pthread_t sp_pthread_t; |
|
124 |
#else
|
|
125 |
typedef pid_t sp_pthread_t; |
|
126 |
#endif
|
|
127 |
||
128 |
struct pam_ctxt { |
|
129 |
sp_pthread_t pam_thread; |
|
130 |
int pam_psock; |
|
131 |
int pam_csock; |
|
132 |
int pam_done; |
|
133 |
};
|
|
134 |
||
135 |
static void sshpam_free_ctx(void *); |
|
136 |
static struct pam_ctxt *cleanup_ctxt; |
|
137 |
||
1.1.2
by Colin Watson
Import upstream version 4.1p1 |
138 |
#ifndef UNSUPPORTED_POSIX_THREADS_HACK
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
139 |
/*
|
140 |
* Simulate threads with processes.
|
|
141 |
*/
|
|
142 |
||
143 |
static int sshpam_thread_status = -1; |
|
144 |
static mysig_t sshpam_oldsig; |
|
145 |
||
1.1.3
by Colin Watson
Import upstream version 4.2p1 |
146 |
static void |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
147 |
sshpam_sigchld_handler(int sig) |
148 |
{
|
|
2
by Colin Watson
* Nathaniel McCallum: |
149 |
signal(SIGCHLD, SIG_DFL); |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
150 |
if (cleanup_ctxt == NULL) |
151 |
return; /* handler called after PAM cleanup, shouldn't happen */ |
|
2
by Colin Watson
* Nathaniel McCallum: |
152 |
if (waitpid(cleanup_ctxt->pam_thread, &sshpam_thread_status, WNOHANG) |
1.1.3
by Colin Watson
Import upstream version 4.2p1 |
153 |
<= 0) { |
2
by Colin Watson
* Nathaniel McCallum: |
154 |
/* PAM thread has not exitted, privsep slave must have */
|
155 |
kill(cleanup_ctxt->pam_thread, SIGTERM); |
|
156 |
if (waitpid(cleanup_ctxt->pam_thread, &sshpam_thread_status, 0) |
|
157 |
<= 0) |
|
158 |
return; /* could not wait */ |
|
159 |
}
|
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
160 |
if (WIFSIGNALED(sshpam_thread_status) && |
161 |
WTERMSIG(sshpam_thread_status) == SIGTERM) |
|
162 |
return; /* terminated by pthread_cancel */ |
|
163 |
if (!WIFEXITED(sshpam_thread_status)) |
|
1.13.2
by Colin Watson
Import upstream version 4.7p1 |
164 |
sigdie("PAM: authentication thread exited unexpectedly"); |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
165 |
if (WEXITSTATUS(sshpam_thread_status) != 0) |
1.13.2
by Colin Watson
Import upstream version 4.7p1 |
166 |
sigdie("PAM: authentication thread exited uncleanly"); |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
167 |
}
|
168 |
||
1.13.1
by Colin Watson
Import upstream version 4.6p1 |
169 |
/* ARGSUSED */
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
170 |
static void |
1.13.1
by Colin Watson
Import upstream version 4.6p1 |
171 |
pthread_exit(void *value) |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
172 |
{
|
173 |
_exit(0); |
|
174 |
}
|
|
175 |
||
1.13.1
by Colin Watson
Import upstream version 4.6p1 |
176 |
/* ARGSUSED */
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
177 |
static int |
1.13.1
by Colin Watson
Import upstream version 4.6p1 |
178 |
pthread_create(sp_pthread_t *thread, const void *attr, |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
179 |
void *(*thread_start)(void *), void *arg) |
180 |
{
|
|
181 |
pid_t pid; |
|
1.1.3
by Colin Watson
Import upstream version 4.2p1 |
182 |
struct pam_ctxt *ctx = arg; |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
183 |
|
184 |
sshpam_thread_status = -1; |
|
185 |
switch ((pid = fork())) { |
|
186 |
case -1: |
|
187 |
error("fork(): %s", strerror(errno)); |
|
188 |
return (-1); |
|
189 |
case 0: |
|
1.1.3
by Colin Watson
Import upstream version 4.2p1 |
190 |
close(ctx->pam_psock); |
191 |
ctx->pam_psock = -1; |
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
192 |
thread_start(arg); |
193 |
_exit(1); |
|
194 |
default: |
|
195 |
*thread = pid; |
|
1.1.3
by Colin Watson
Import upstream version 4.2p1 |
196 |
close(ctx->pam_csock); |
197 |
ctx->pam_csock = -1; |
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
198 |
sshpam_oldsig = signal(SIGCHLD, sshpam_sigchld_handler); |
199 |
return (0); |
|
200 |
}
|
|
201 |
}
|
|
202 |
||
203 |
static int |
|
204 |
pthread_cancel(sp_pthread_t thread) |
|
205 |
{
|
|
206 |
signal(SIGCHLD, sshpam_oldsig); |
|
207 |
return (kill(thread, SIGTERM)); |
|
208 |
}
|
|
209 |
||
1.13.1
by Colin Watson
Import upstream version 4.6p1 |
210 |
/* ARGSUSED */
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
211 |
static int |
1.13.1
by Colin Watson
Import upstream version 4.6p1 |
212 |
pthread_join(sp_pthread_t thread, void **value) |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
213 |
{
|
214 |
int status; |
|
215 |
||
216 |
if (sshpam_thread_status != -1) |
|
217 |
return (sshpam_thread_status); |
|
218 |
signal(SIGCHLD, sshpam_oldsig); |
|
219 |
waitpid(thread, &status, 0); |
|
220 |
return (status); |
|
221 |
}
|
|
222 |
#endif
|
|
223 |
||
224 |
||
225 |
static pam_handle_t *sshpam_handle = NULL; |
|
226 |
static int sshpam_err = 0; |
|
227 |
static int sshpam_authenticated = 0; |
|
228 |
static int sshpam_session_open = 0; |
|
229 |
static int sshpam_cred_established = 0; |
|
230 |
static int sshpam_account_status = -1; |
|
231 |
static char **sshpam_env = NULL; |
|
232 |
static Authctxt *sshpam_authctxt = NULL; |
|
1.1.1
by Colin Watson
Import upstream version 3.9p1 |
233 |
static const char *sshpam_password = NULL; |
3
by Colin Watson
Don't ask unnecessary and misplaced ssh/forward_warning debconf note |
234 |
static char badpw[] = "\b\n\r\177INCORRECT"; |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
235 |
|
236 |
/* Some PAM implementations don't implement this */
|
|
237 |
#ifndef HAVE_PAM_GETENVLIST
|
|
238 |
static char ** |
|
239 |
pam_getenvlist(pam_handle_t *pamh) |
|
240 |
{
|
|
241 |
/*
|
|
242 |
* XXX - If necessary, we can still support envrionment passing
|
|
243 |
* for platforms without pam_getenvlist by searching for known
|
|
244 |
* env vars (e.g. KRB5CCNAME) from the PAM environment.
|
|
245 |
*/
|
|
246 |
return NULL; |
|
247 |
}
|
|
248 |
#endif
|
|
249 |
||
1.1.1
by Colin Watson
Import upstream version 3.9p1 |
250 |
/*
|
251 |
* Some platforms, notably Solaris, do not enforce password complexity
|
|
252 |
* rules during pam_chauthtok() if the real uid of the calling process
|
|
253 |
* is 0, on the assumption that it's being called by "passwd" run by root.
|
|
254 |
* This wraps pam_chauthtok and sets/restore the real uid so PAM will do
|
|
255 |
* the right thing.
|
|
256 |
*/
|
|
257 |
#ifdef SSHPAM_CHAUTHTOK_NEEDS_RUID
|
|
258 |
static int |
|
259 |
sshpam_chauthtok_ruid(pam_handle_t *pamh, int flags) |
|
260 |
{
|
|
261 |
int result; |
|
262 |
||
263 |
if (sshpam_authctxt == NULL) |
|
264 |
fatal("PAM: sshpam_authctxt not initialized"); |
|
265 |
if (setreuid(sshpam_authctxt->pw->pw_uid, -1) == -1) |
|
266 |
fatal("%s: setreuid failed: %s", __func__, strerror(errno)); |
|
267 |
result = pam_chauthtok(pamh, flags); |
|
268 |
if (setreuid(0, -1) == -1) |
|
269 |
fatal("%s: setreuid failed: %s", __func__, strerror(errno)); |
|
270 |
return result; |
|
271 |
}
|
|
272 |
# define pam_chauthtok(a,b) (sshpam_chauthtok_ruid((a), (b)))
|
|
273 |
#endif
|
|
274 |
||
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
275 |
void
|
1.1.1
by Colin Watson
Import upstream version 3.9p1 |
276 |
sshpam_password_change_required(int reqd) |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
277 |
{
|
278 |
debug3("%s %d", __func__, reqd); |
|
279 |
if (sshpam_authctxt == NULL) |
|
280 |
fatal("%s: PAM authctxt not initialized", __func__); |
|
281 |
sshpam_authctxt->force_pwchange = reqd; |
|
282 |
if (reqd) { |
|
283 |
no_port_forwarding_flag |= 2; |
|
284 |
no_agent_forwarding_flag |= 2; |
|
285 |
no_x11_forwarding_flag |= 2; |
|
286 |
} else { |
|
287 |
no_port_forwarding_flag &= ~2; |
|
288 |
no_agent_forwarding_flag &= ~2; |
|
289 |
no_x11_forwarding_flag &= ~2; |
|
290 |
}
|
|
291 |
}
|
|
292 |
||
293 |
/* Import regular and PAM environment from subprocess */
|
|
294 |
static void |
|
295 |
import_environments(Buffer *b) |
|
296 |
{
|
|
297 |
char *env; |
|
298 |
u_int i, num_env; |
|
299 |
int err; |
|
300 |
||
301 |
debug3("PAM: %s entering", __func__); |
|
302 |
||
1.1.2
by Colin Watson
Import upstream version 4.1p1 |
303 |
#ifndef UNSUPPORTED_POSIX_THREADS_HACK
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
304 |
/* Import variables set by do_pam_account */
|
305 |
sshpam_account_status = buffer_get_int(b); |
|
1.1.1
by Colin Watson
Import upstream version 3.9p1 |
306 |
sshpam_password_change_required(buffer_get_int(b)); |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
307 |
|
308 |
/* Import environment from subprocess */
|
|
309 |
num_env = buffer_get_int(b); |
|
1.13.1
by Colin Watson
Import upstream version 4.6p1 |
310 |
if (num_env > 1024) |
311 |
fatal("%s: received %u environment variables, expected <= 1024", |
|
312 |
__func__, num_env); |
|
313 |
sshpam_env = xcalloc(num_env + 1, sizeof(*sshpam_env)); |
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
314 |
debug3("PAM: num env strings %d", num_env); |
315 |
for(i = 0; i < num_env; i++) |
|
316 |
sshpam_env[i] = buffer_get_string(b, NULL); |
|
317 |
||
318 |
sshpam_env[num_env] = NULL; |
|
319 |
||
320 |
/* Import PAM environment from subprocess */
|
|
321 |
num_env = buffer_get_int(b); |
|
322 |
debug("PAM: num PAM env strings %d", num_env); |
|
323 |
for(i = 0; i < num_env; i++) { |
|
324 |
env = buffer_get_string(b, NULL); |
|
325 |
||
326 |
#ifdef HAVE_PAM_PUTENV
|
|
327 |
/* Errors are not fatal here */
|
|
328 |
if ((err = pam_putenv(sshpam_handle, env)) != PAM_SUCCESS) { |
|
329 |
error("PAM: pam_putenv: %s", |
|
330 |
pam_strerror(sshpam_handle, sshpam_err)); |
|
331 |
}
|
|
332 |
#endif
|
|
333 |
}
|
|
334 |
#endif
|
|
335 |
}
|
|
336 |
||
337 |
/*
|
|
338 |
* Conversation function for authentication thread.
|
|
339 |
*/
|
|
340 |
static int |
|
1.1.3
by Colin Watson
Import upstream version 4.2p1 |
341 |
sshpam_thread_conv(int n, sshpam_const struct pam_message **msg, |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
342 |
struct pam_response **resp, void *data) |
343 |
{
|
|
344 |
Buffer buffer; |
|
345 |
struct pam_ctxt *ctxt; |
|
346 |
struct pam_response *reply; |
|
347 |
int i; |
|
348 |
||
349 |
debug3("PAM: %s entering, %d messages", __func__, n); |
|
350 |
*resp = NULL; |
|
351 |
||
1.1.1
by Colin Watson
Import upstream version 3.9p1 |
352 |
if (data == NULL) { |
353 |
error("PAM: conversation function passed a null context"); |
|
354 |
return (PAM_CONV_ERR); |
|
355 |
}
|
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
356 |
ctxt = data; |
357 |
if (n <= 0 || n > PAM_MAX_NUM_MSG) |
|
358 |
return (PAM_CONV_ERR); |
|
359 |
||
1.13.1
by Colin Watson
Import upstream version 4.6p1 |
360 |
if ((reply = calloc(n, sizeof(*reply))) == NULL) |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
361 |
return (PAM_CONV_ERR); |
362 |
||
363 |
buffer_init(&buffer); |
|
364 |
for (i = 0; i < n; ++i) { |
|
365 |
switch (PAM_MSG_MEMBER(msg, i, msg_style)) { |
|
366 |
case PAM_PROMPT_ECHO_OFF: |
|
367 |
buffer_put_cstring(&buffer, |
|
368 |
PAM_MSG_MEMBER(msg, i, msg)); |
|
369 |
if (ssh_msg_send(ctxt->pam_csock, |
|
370 |
PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1) |
|
371 |
goto fail; |
|
372 |
if (ssh_msg_recv(ctxt->pam_csock, &buffer) == -1) |
|
373 |
goto fail; |
|
374 |
if (buffer_get_char(&buffer) != PAM_AUTHTOK) |
|
375 |
goto fail; |
|
376 |
reply[i].resp = buffer_get_string(&buffer, NULL); |
|
377 |
break; |
|
378 |
case PAM_PROMPT_ECHO_ON: |
|
379 |
buffer_put_cstring(&buffer, |
|
380 |
PAM_MSG_MEMBER(msg, i, msg)); |
|
381 |
if (ssh_msg_send(ctxt->pam_csock, |
|
382 |
PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1) |
|
383 |
goto fail; |
|
384 |
if (ssh_msg_recv(ctxt->pam_csock, &buffer) == -1) |
|
385 |
goto fail; |
|
386 |
if (buffer_get_char(&buffer) != PAM_AUTHTOK) |
|
387 |
goto fail; |
|
388 |
reply[i].resp = buffer_get_string(&buffer, NULL); |
|
389 |
break; |
|
390 |
case PAM_ERROR_MSG: |
|
391 |
buffer_put_cstring(&buffer, |
|
392 |
PAM_MSG_MEMBER(msg, i, msg)); |
|
393 |
if (ssh_msg_send(ctxt->pam_csock, |
|
394 |
PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1) |
|
395 |
goto fail; |
|
396 |
break; |
|
397 |
case PAM_TEXT_INFO: |
|
398 |
buffer_put_cstring(&buffer, |
|
399 |
PAM_MSG_MEMBER(msg, i, msg)); |
|
400 |
if (ssh_msg_send(ctxt->pam_csock, |
|
401 |
PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1) |
|
402 |
goto fail; |
|
403 |
break; |
|
404 |
default: |
|
405 |
goto fail; |
|
406 |
}
|
|
407 |
buffer_clear(&buffer); |
|
408 |
}
|
|
409 |
buffer_free(&buffer); |
|
410 |
*resp = reply; |
|
411 |
return (PAM_SUCCESS); |
|
412 |
||
413 |
fail: |
|
414 |
for(i = 0; i < n; i++) { |
|
415 |
if (reply[i].resp != NULL) |
|
416 |
xfree(reply[i].resp); |
|
417 |
}
|
|
418 |
xfree(reply); |
|
419 |
buffer_free(&buffer); |
|
420 |
return (PAM_CONV_ERR); |
|
421 |
}
|
|
422 |
||
423 |
/*
|
|
424 |
* Authentication thread.
|
|
425 |
*/
|
|
426 |
static void * |
|
427 |
sshpam_thread(void *ctxtp) |
|
428 |
{
|
|
429 |
struct pam_ctxt *ctxt = ctxtp; |
|
430 |
Buffer buffer; |
|
431 |
struct pam_conv sshpam_conv; |
|
1.1.1
by Colin Watson
Import upstream version 3.9p1 |
432 |
int flags = (options.permit_empty_passwd == 0 ? |
433 |
PAM_DISALLOW_NULL_AUTHTOK : 0); |
|
1.1.2
by Colin Watson
Import upstream version 4.1p1 |
434 |
#ifndef UNSUPPORTED_POSIX_THREADS_HACK
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
435 |
extern char **environ; |
436 |
char **env_from_pam; |
|
437 |
u_int i; |
|
438 |
const char *pam_user; |
|
1.1.3
by Colin Watson
Import upstream version 4.2p1 |
439 |
const char **ptr_pam_user = &pam_user; |
1.13.1
by Colin Watson
Import upstream version 4.6p1 |
440 |
char *tz = getenv("TZ"); |
441 |
||
1.1.3
by Colin Watson
Import upstream version 4.2p1 |
442 |
pam_get_item(sshpam_handle, PAM_USER, |
443 |
(sshpam_const void **)ptr_pam_user); |
|
1.13.1
by Colin Watson
Import upstream version 4.6p1 |
444 |
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
445 |
environ[0] = NULL; |
1.13.1
by Colin Watson
Import upstream version 4.6p1 |
446 |
if (tz != NULL) |
447 |
if (setenv("TZ", tz, 1) == -1) |
|
448 |
error("PAM: could not set TZ environment: %s", |
|
449 |
strerror(errno)); |
|
1.1.1
by Colin Watson
Import upstream version 3.9p1 |
450 |
|
451 |
if (sshpam_authctxt != NULL) { |
|
452 |
setproctitle("%s [pam]", |
|
453 |
sshpam_authctxt->valid ? pam_user : "unknown"); |
|
454 |
}
|
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
455 |
#endif
|
456 |
||
457 |
sshpam_conv.conv = sshpam_thread_conv; |
|
458 |
sshpam_conv.appdata_ptr = ctxt; |
|
459 |
||
460 |
if (sshpam_authctxt == NULL) |
|
461 |
fatal("%s: PAM authctxt not initialized", __func__); |
|
462 |
||
463 |
buffer_init(&buffer); |
|
464 |
sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, |
|
465 |
(const void *)&sshpam_conv); |
|
466 |
if (sshpam_err != PAM_SUCCESS) |
|
467 |
goto auth_fail; |
|
1.1.1
by Colin Watson
Import upstream version 3.9p1 |
468 |
sshpam_err = pam_authenticate(sshpam_handle, flags); |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
469 |
if (sshpam_err != PAM_SUCCESS) |
470 |
goto auth_fail; |
|
471 |
||
472 |
if (compat20) { |
|
1.13.1
by Colin Watson
Import upstream version 4.6p1 |
473 |
if (!do_pam_account()) { |
474 |
sshpam_err = PAM_ACCT_EXPIRED; |
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
475 |
goto auth_fail; |
1.13.1
by Colin Watson
Import upstream version 4.6p1 |
476 |
}
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
477 |
if (sshpam_authctxt->force_pwchange) { |
478 |
sshpam_err = pam_chauthtok(sshpam_handle, |
|
479 |
PAM_CHANGE_EXPIRED_AUTHTOK); |
|
480 |
if (sshpam_err != PAM_SUCCESS) |
|
481 |
goto auth_fail; |
|
1.1.1
by Colin Watson
Import upstream version 3.9p1 |
482 |
sshpam_password_change_required(0); |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
483 |
}
|
484 |
}
|
|
485 |
||
486 |
buffer_put_cstring(&buffer, "OK"); |
|
487 |
||
1.1.2
by Colin Watson
Import upstream version 4.1p1 |
488 |
#ifndef UNSUPPORTED_POSIX_THREADS_HACK
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
489 |
/* Export variables set by do_pam_account */
|
490 |
buffer_put_int(&buffer, sshpam_account_status); |
|
491 |
buffer_put_int(&buffer, sshpam_authctxt->force_pwchange); |
|
492 |
||
493 |
/* Export any environment strings set in child */
|
|
494 |
for(i = 0; environ[i] != NULL; i++) |
|
495 |
; /* Count */ |
|
496 |
buffer_put_int(&buffer, i); |
|
497 |
for(i = 0; environ[i] != NULL; i++) |
|
498 |
buffer_put_cstring(&buffer, environ[i]); |
|
499 |
||
500 |
/* Export any environment strings set by PAM in child */
|
|
501 |
env_from_pam = pam_getenvlist(sshpam_handle); |
|
502 |
for(i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++) |
|
503 |
; /* Count */ |
|
504 |
buffer_put_int(&buffer, i); |
|
505 |
for(i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++) |
|
506 |
buffer_put_cstring(&buffer, env_from_pam[i]); |
|
1.1.2
by Colin Watson
Import upstream version 4.1p1 |
507 |
#endif /* UNSUPPORTED_POSIX_THREADS_HACK */ |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
508 |
|
509 |
/* XXX - can't do much about an error here */
|
|
510 |
ssh_msg_send(ctxt->pam_csock, sshpam_err, &buffer); |
|
511 |
buffer_free(&buffer); |
|
512 |
pthread_exit(NULL); |
|
513 |
||
514 |
auth_fail: |
|
515 |
buffer_put_cstring(&buffer, |
|
516 |
pam_strerror(sshpam_handle, sshpam_err)); |
|
517 |
/* XXX - can't do much about an error here */
|
|
1.13.1
by Colin Watson
Import upstream version 4.6p1 |
518 |
if (sshpam_err == PAM_ACCT_EXPIRED) |
519 |
ssh_msg_send(ctxt->pam_csock, PAM_ACCT_EXPIRED, &buffer); |
|
520 |
else
|
|
521 |
ssh_msg_send(ctxt->pam_csock, PAM_AUTH_ERR, &buffer); |
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
522 |
buffer_free(&buffer); |
523 |
pthread_exit(NULL); |
|
524 |
||
525 |
return (NULL); /* Avoid warning for non-pthread case */ |
|
526 |
}
|
|
527 |
||
528 |
void
|
|
529 |
sshpam_thread_cleanup(void) |
|
530 |
{
|
|
531 |
struct pam_ctxt *ctxt = cleanup_ctxt; |
|
532 |
||
533 |
debug3("PAM: %s entering", __func__); |
|
534 |
if (ctxt != NULL && ctxt->pam_thread != 0) { |
|
535 |
pthread_cancel(ctxt->pam_thread); |
|
536 |
pthread_join(ctxt->pam_thread, NULL); |
|
537 |
close(ctxt->pam_psock); |
|
538 |
close(ctxt->pam_csock); |
|
539 |
memset(ctxt, 0, sizeof(*ctxt)); |
|
540 |
cleanup_ctxt = NULL; |
|
541 |
}
|
|
542 |
}
|
|
543 |
||
544 |
static int |
|
1.1.3
by Colin Watson
Import upstream version 4.2p1 |
545 |
sshpam_null_conv(int n, sshpam_const struct pam_message **msg, |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
546 |
struct pam_response **resp, void *data) |
547 |
{
|
|
548 |
debug3("PAM: %s entering, %d messages", __func__, n); |
|
549 |
return (PAM_CONV_ERR); |
|
550 |
}
|
|
551 |
||
552 |
static struct pam_conv null_conv = { sshpam_null_conv, NULL }; |
|
553 |
||
1.1.2
by Colin Watson
Import upstream version 4.1p1 |
554 |
static int |
1.1.3
by Colin Watson
Import upstream version 4.2p1 |
555 |
sshpam_store_conv(int n, sshpam_const struct pam_message **msg, |
1.1.2
by Colin Watson
Import upstream version 4.1p1 |
556 |
struct pam_response **resp, void *data) |
557 |
{
|
|
558 |
struct pam_response *reply; |
|
559 |
int i; |
|
560 |
size_t len; |
|
561 |
||
562 |
debug3("PAM: %s called with %d messages", __func__, n); |
|
563 |
*resp = NULL; |
|
564 |
||
565 |
if (n <= 0 || n > PAM_MAX_NUM_MSG) |
|
566 |
return (PAM_CONV_ERR); |
|
567 |
||
1.13.1
by Colin Watson
Import upstream version 4.6p1 |
568 |
if ((reply = calloc(n, sizeof(*reply))) == NULL) |
1.1.2
by Colin Watson
Import upstream version 4.1p1 |
569 |
return (PAM_CONV_ERR); |
570 |
||
571 |
for (i = 0; i < n; ++i) { |
|
572 |
switch (PAM_MSG_MEMBER(msg, i, msg_style)) { |
|
573 |
case PAM_ERROR_MSG: |
|
574 |
case PAM_TEXT_INFO: |
|
575 |
len = strlen(PAM_MSG_MEMBER(msg, i, msg)); |
|
576 |
buffer_append(&loginmsg, PAM_MSG_MEMBER(msg, i, msg), len); |
|
577 |
buffer_append(&loginmsg, "\n", 1 ); |
|
578 |
reply[i].resp_retcode = PAM_SUCCESS; |
|
579 |
break; |
|
580 |
default: |
|
581 |
goto fail; |
|
582 |
}
|
|
583 |
}
|
|
584 |
*resp = reply; |
|
585 |
return (PAM_SUCCESS); |
|
586 |
||
587 |
fail: |
|
588 |
for(i = 0; i < n; i++) { |
|
589 |
if (reply[i].resp != NULL) |
|
590 |
xfree(reply[i].resp); |
|
591 |
}
|
|
592 |
xfree(reply); |
|
593 |
return (PAM_CONV_ERR); |
|
594 |
}
|
|
595 |
||
596 |
static struct pam_conv store_conv = { sshpam_store_conv, NULL }; |
|
597 |
||
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
598 |
void
|
599 |
sshpam_cleanup(void) |
|
600 |
{
|
|
1.13.3
by Colin Watson
Import upstream version 5.1p1 |
601 |
if (sshpam_handle == NULL || (use_privsep && !mm_is_monitor())) |
602 |
return; |
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
603 |
debug("PAM: cleanup"); |
604 |
pam_set_item(sshpam_handle, PAM_CONV, (const void *)&null_conv); |
|
1.13.5
by Colin Watson
Import upstream version 5.3p1 |
605 |
if (sshpam_session_open) { |
606 |
debug("PAM: closing session"); |
|
607 |
pam_close_session(sshpam_handle, PAM_SILENT); |
|
608 |
sshpam_session_open = 0; |
|
609 |
}
|
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
610 |
if (sshpam_cred_established) { |
1.13.3
by Colin Watson
Import upstream version 5.1p1 |
611 |
debug("PAM: deleting credentials"); |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
612 |
pam_setcred(sshpam_handle, PAM_DELETE_CRED); |
613 |
sshpam_cred_established = 0; |
|
614 |
}
|
|
615 |
sshpam_authenticated = 0; |
|
616 |
pam_end(sshpam_handle, sshpam_err); |
|
617 |
sshpam_handle = NULL; |
|
618 |
}
|
|
619 |
||
620 |
static int |
|
621 |
sshpam_init(Authctxt *authctxt) |
|
622 |
{
|
|
623 |
extern char *__progname; |
|
624 |
const char *pam_rhost, *pam_user, *user = authctxt->user; |
|
1.1.3
by Colin Watson
Import upstream version 4.2p1 |
625 |
const char **ptr_pam_user = &pam_user; |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
626 |
|
627 |
if (sshpam_handle != NULL) { |
|
628 |
/* We already have a PAM context; check if the user matches */
|
|
629 |
sshpam_err = pam_get_item(sshpam_handle, |
|
1.1.3
by Colin Watson
Import upstream version 4.2p1 |
630 |
PAM_USER, (sshpam_const void **)ptr_pam_user); |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
631 |
if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0) |
632 |
return (0); |
|
633 |
pam_end(sshpam_handle, sshpam_err); |
|
634 |
sshpam_handle = NULL; |
|
635 |
}
|
|
636 |
debug("PAM: initializing for \"%s\"", user); |
|
637 |
sshpam_err = |
|
1.1.2
by Colin Watson
Import upstream version 4.1p1 |
638 |
pam_start(SSHD_PAM_SERVICE, user, &store_conv, &sshpam_handle); |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
639 |
sshpam_authctxt = authctxt; |
640 |
||
641 |
if (sshpam_err != PAM_SUCCESS) { |
|
642 |
pam_end(sshpam_handle, sshpam_err); |
|
643 |
sshpam_handle = NULL; |
|
644 |
return (-1); |
|
645 |
}
|
|
646 |
pam_rhost = get_remote_name_or_ip(utmp_len, options.use_dns); |
|
647 |
debug("PAM: setting PAM_RHOST to \"%s\"", pam_rhost); |
|
648 |
sshpam_err = pam_set_item(sshpam_handle, PAM_RHOST, pam_rhost); |
|
649 |
if (sshpam_err != PAM_SUCCESS) { |
|
650 |
pam_end(sshpam_handle, sshpam_err); |
|
651 |
sshpam_handle = NULL; |
|
652 |
return (-1); |
|
653 |
}
|
|
654 |
#ifdef PAM_TTY_KLUDGE
|
|
655 |
/*
|
|
656 |
* Some silly PAM modules (e.g. pam_time) require a TTY to operate.
|
|
657 |
* sshd doesn't set the tty until too late in the auth process and
|
|
658 |
* may not even set one (for tty-less connections)
|
|
659 |
*/
|
|
660 |
debug("PAM: setting PAM_TTY to \"ssh\""); |
|
661 |
sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, "ssh"); |
|
662 |
if (sshpam_err != PAM_SUCCESS) { |
|
663 |
pam_end(sshpam_handle, sshpam_err); |
|
664 |
sshpam_handle = NULL; |
|
665 |
return (-1); |
|
666 |
}
|
|
667 |
#endif
|
|
668 |
return (0); |
|
669 |
}
|
|
670 |
||
671 |
static void * |
|
672 |
sshpam_init_ctx(Authctxt *authctxt) |
|
673 |
{
|
|
674 |
struct pam_ctxt *ctxt; |
|
675 |
int socks[2]; |
|
676 |
||
677 |
debug3("PAM: %s entering", __func__); |
|
1.13.1
by Colin Watson
Import upstream version 4.6p1 |
678 |
/*
|
679 |
* Refuse to start if we don't have PAM enabled or do_pam_account
|
|
680 |
* has previously failed.
|
|
681 |
*/
|
|
682 |
if (!options.use_pam || sshpam_account_status == 0) |
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
683 |
return NULL; |
684 |
||
685 |
/* Initialize PAM */
|
|
686 |
if (sshpam_init(authctxt) == -1) { |
|
687 |
error("PAM: initialization failed"); |
|
688 |
return (NULL); |
|
689 |
}
|
|
690 |
||
1.13.2
by Colin Watson
Import upstream version 4.7p1 |
691 |
ctxt = xcalloc(1, sizeof *ctxt); |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
692 |
|
693 |
/* Start the authentication thread */
|
|
694 |
if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, socks) == -1) { |
|
695 |
error("PAM: failed create sockets: %s", strerror(errno)); |
|
696 |
xfree(ctxt); |
|
697 |
return (NULL); |
|
698 |
}
|
|
699 |
ctxt->pam_psock = socks[0]; |
|
700 |
ctxt->pam_csock = socks[1]; |
|
701 |
if (pthread_create(&ctxt->pam_thread, NULL, sshpam_thread, ctxt) == -1) { |
|
702 |
error("PAM: failed to start authentication thread: %s", |
|
703 |
strerror(errno)); |
|
704 |
close(socks[0]); |
|
705 |
close(socks[1]); |
|
706 |
xfree(ctxt); |
|
707 |
return (NULL); |
|
708 |
}
|
|
709 |
cleanup_ctxt = ctxt; |
|
710 |
return (ctxt); |
|
711 |
}
|
|
712 |
||
713 |
static int |
|
714 |
sshpam_query(void *ctx, char **name, char **info, |
|
715 |
u_int *num, char ***prompts, u_int **echo_on) |
|
716 |
{
|
|
717 |
Buffer buffer; |
|
718 |
struct pam_ctxt *ctxt = ctx; |
|
719 |
size_t plen; |
|
720 |
u_char type; |
|
721 |
char *msg; |
|
1.1.2
by Colin Watson
Import upstream version 4.1p1 |
722 |
size_t len, mlen; |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
723 |
|
724 |
debug3("PAM: %s entering", __func__); |
|
725 |
buffer_init(&buffer); |
|
726 |
*name = xstrdup(""); |
|
727 |
*info = xstrdup(""); |
|
728 |
*prompts = xmalloc(sizeof(char *)); |
|
729 |
**prompts = NULL; |
|
730 |
plen = 0; |
|
731 |
*echo_on = xmalloc(sizeof(u_int)); |
|
732 |
while (ssh_msg_recv(ctxt->pam_psock, &buffer) == 0) { |
|
733 |
type = buffer_get_char(&buffer); |
|
734 |
msg = buffer_get_string(&buffer, NULL); |
|
1.1.2
by Colin Watson
Import upstream version 4.1p1 |
735 |
mlen = strlen(msg); |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
736 |
switch (type) { |
737 |
case PAM_PROMPT_ECHO_ON: |
|
738 |
case PAM_PROMPT_ECHO_OFF: |
|
739 |
*num = 1; |
|
1.1.2
by Colin Watson
Import upstream version 4.1p1 |
740 |
len = plen + mlen + 1; |
1.13.1
by Colin Watson
Import upstream version 4.6p1 |
741 |
**prompts = xrealloc(**prompts, 1, len); |
1.1.2
by Colin Watson
Import upstream version 4.1p1 |
742 |
strlcpy(**prompts + plen, msg, len - plen); |
743 |
plen += mlen; |
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
744 |
**echo_on = (type == PAM_PROMPT_ECHO_ON); |
745 |
xfree(msg); |
|
746 |
return (0); |
|
747 |
case PAM_ERROR_MSG: |
|
748 |
case PAM_TEXT_INFO: |
|
749 |
/* accumulate messages */
|
|
1.1.2
by Colin Watson
Import upstream version 4.1p1 |
750 |
len = plen + mlen + 2; |
1.13.1
by Colin Watson
Import upstream version 4.6p1 |
751 |
**prompts = xrealloc(**prompts, 1, len); |
1.1.2
by Colin Watson
Import upstream version 4.1p1 |
752 |
strlcpy(**prompts + plen, msg, len - plen); |
753 |
plen += mlen; |
|
754 |
strlcat(**prompts + plen, "\n", len - plen); |
|
755 |
plen++; |
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
756 |
xfree(msg); |
757 |
break; |
|
1.13.1
by Colin Watson
Import upstream version 4.6p1 |
758 |
case PAM_ACCT_EXPIRED: |
759 |
sshpam_account_status = 0; |
|
760 |
/* FALLTHROUGH */
|
|
1.6.1
by Colin Watson
Import upstream version 4.3p2 |
761 |
case PAM_AUTH_ERR: |
1.13.1
by Colin Watson
Import upstream version 4.6p1 |
762 |
debug3("PAM: %s", pam_strerror(sshpam_handle, type)); |
1.6.1
by Colin Watson
Import upstream version 4.3p2 |
763 |
if (**prompts != NULL && strlen(**prompts) != 0) { |
764 |
*info = **prompts; |
|
765 |
**prompts = NULL; |
|
766 |
*num = 0; |
|
767 |
**echo_on = 0; |
|
768 |
ctxt->pam_done = -1; |
|
1.13.1
by Colin Watson
Import upstream version 4.6p1 |
769 |
xfree(msg); |
1.6.1
by Colin Watson
Import upstream version 4.3p2 |
770 |
return 0; |
771 |
}
|
|
772 |
/* FALLTHROUGH */
|
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
773 |
case PAM_SUCCESS: |
774 |
if (**prompts != NULL) { |
|
775 |
/* drain any accumulated messages */
|
|
776 |
debug("PAM: %s", **prompts); |
|
777 |
buffer_append(&loginmsg, **prompts, |
|
778 |
strlen(**prompts)); |
|
779 |
xfree(**prompts); |
|
780 |
**prompts = NULL; |
|
781 |
}
|
|
782 |
if (type == PAM_SUCCESS) { |
|
1.1.2
by Colin Watson
Import upstream version 4.1p1 |
783 |
if (!sshpam_authctxt->valid || |
784 |
(sshpam_authctxt->pw->pw_uid == 0 && |
|
785 |
options.permit_root_login != PERMIT_YES)) |
|
786 |
fatal("Internal error: PAM auth " |
|
787 |
"succeeded when it should have "
|
|
788 |
"failed"); |
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
789 |
import_environments(&buffer); |
790 |
*num = 0; |
|
791 |
**echo_on = 0; |
|
792 |
ctxt->pam_done = 1; |
|
793 |
xfree(msg); |
|
794 |
return (0); |
|
795 |
}
|
|
796 |
error("PAM: %s for %s%.100s from %.100s", msg, |
|
797 |
sshpam_authctxt->valid ? "" : "illegal user ", |
|
798 |
sshpam_authctxt->user, |
|
799 |
get_remote_name_or_ip(utmp_len, options.use_dns)); |
|
800 |
/* FALLTHROUGH */
|
|
801 |
default: |
|
802 |
*num = 0; |
|
803 |
**echo_on = 0; |
|
804 |
xfree(msg); |
|
805 |
ctxt->pam_done = -1; |
|
806 |
return (-1); |
|
807 |
}
|
|
808 |
}
|
|
809 |
return (-1); |
|
810 |
}
|
|
811 |
||
812 |
/* XXX - see also comment in auth-chall.c:verify_response */
|
|
813 |
static int |
|
814 |
sshpam_respond(void *ctx, u_int num, char **resp) |
|
815 |
{
|
|
816 |
Buffer buffer; |
|
817 |
struct pam_ctxt *ctxt = ctx; |
|
818 |
||
1.6.1
by Colin Watson
Import upstream version 4.3p2 |
819 |
debug2("PAM: %s entering, %u responses", __func__, num); |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
820 |
switch (ctxt->pam_done) { |
821 |
case 1: |
|
822 |
sshpam_authenticated = 1; |
|
823 |
return (0); |
|
824 |
case 0: |
|
825 |
break; |
|
826 |
default: |
|
827 |
return (-1); |
|
828 |
}
|
|
829 |
if (num != 1) { |
|
830 |
error("PAM: expected one response, got %u", num); |
|
831 |
return (-1); |
|
832 |
}
|
|
833 |
buffer_init(&buffer); |
|
3
by Colin Watson
Don't ask unnecessary and misplaced ssh/forward_warning debconf note |
834 |
if (sshpam_authctxt->valid && |
835 |
(sshpam_authctxt->pw->pw_uid != 0 || |
|
1.1.3
by Colin Watson
Import upstream version 4.2p1 |
836 |
options.permit_root_login == PERMIT_YES)) |
3
by Colin Watson
Don't ask unnecessary and misplaced ssh/forward_warning debconf note |
837 |
buffer_put_cstring(&buffer, *resp); |
838 |
else
|
|
839 |
buffer_put_cstring(&buffer, badpw); |
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
840 |
if (ssh_msg_send(ctxt->pam_psock, PAM_AUTHTOK, &buffer) == -1) { |
841 |
buffer_free(&buffer); |
|
842 |
return (-1); |
|
843 |
}
|
|
844 |
buffer_free(&buffer); |
|
845 |
return (1); |
|
846 |
}
|
|
847 |
||
848 |
static void |
|
849 |
sshpam_free_ctx(void *ctxtp) |
|
850 |
{
|
|
851 |
struct pam_ctxt *ctxt = ctxtp; |
|
852 |
||
853 |
debug3("PAM: %s entering", __func__); |
|
854 |
sshpam_thread_cleanup(); |
|
855 |
xfree(ctxt); |
|
856 |
/*
|
|
857 |
* We don't call sshpam_cleanup() here because we may need the PAM
|
|
858 |
* handle at a later stage, e.g. when setting up a session. It's
|
|
859 |
* still on the cleanup list, so pam_end() *will* be called before
|
|
860 |
* the server process terminates.
|
|
861 |
*/
|
|
862 |
}
|
|
863 |
||
864 |
KbdintDevice sshpam_device = { |
|
865 |
"pam", |
|
866 |
sshpam_init_ctx, |
|
867 |
sshpam_query, |
|
868 |
sshpam_respond, |
|
869 |
sshpam_free_ctx
|
|
870 |
};
|
|
871 |
||
872 |
KbdintDevice mm_sshpam_device = { |
|
873 |
"pam", |
|
874 |
mm_sshpam_init_ctx, |
|
875 |
mm_sshpam_query, |
|
876 |
mm_sshpam_respond, |
|
877 |
mm_sshpam_free_ctx
|
|
878 |
};
|
|
879 |
||
880 |
/*
|
|
881 |
* This replaces auth-pam.c
|
|
882 |
*/
|
|
883 |
void
|
|
884 |
start_pam(Authctxt *authctxt) |
|
885 |
{
|
|
886 |
if (!options.use_pam) |
|
887 |
fatal("PAM: initialisation requested when UsePAM=no"); |
|
888 |
||
889 |
if (sshpam_init(authctxt) == -1) |
|
890 |
fatal("PAM: initialisation failed"); |
|
891 |
}
|
|
892 |
||
893 |
void
|
|
894 |
finish_pam(void) |
|
895 |
{
|
|
896 |
sshpam_cleanup(); |
|
897 |
}
|
|
898 |
||
899 |
u_int
|
|
900 |
do_pam_account(void) |
|
901 |
{
|
|
1.1.2
by Colin Watson
Import upstream version 4.1p1 |
902 |
debug("%s: called", __func__); |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
903 |
if (sshpam_account_status != -1) |
904 |
return (sshpam_account_status); |
|
905 |
||
906 |
sshpam_err = pam_acct_mgmt(sshpam_handle, 0); |
|
1.1.2
by Colin Watson
Import upstream version 4.1p1 |
907 |
debug3("PAM: %s pam_acct_mgmt = %d (%s)", __func__, sshpam_err, |
908 |
pam_strerror(sshpam_handle, sshpam_err)); |
|
1.1.3
by Colin Watson
Import upstream version 4.2p1 |
909 |
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
910 |
if (sshpam_err != PAM_SUCCESS && sshpam_err != PAM_NEW_AUTHTOK_REQD) { |
911 |
sshpam_account_status = 0; |
|
912 |
return (sshpam_account_status); |
|
913 |
}
|
|
914 |
||
915 |
if (sshpam_err == PAM_NEW_AUTHTOK_REQD) |
|
1.1.1
by Colin Watson
Import upstream version 3.9p1 |
916 |
sshpam_password_change_required(1); |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
917 |
|
918 |
sshpam_account_status = 1; |
|
919 |
return (sshpam_account_status); |
|
920 |
}
|
|
921 |
||
922 |
void
|
|
923 |
do_pam_set_tty(const char *tty) |
|
924 |
{
|
|
925 |
if (tty != NULL) { |
|
926 |
debug("PAM: setting PAM_TTY to \"%s\"", tty); |
|
927 |
sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, tty); |
|
928 |
if (sshpam_err != PAM_SUCCESS) |
|
929 |
fatal("PAM: failed to set PAM_TTY: %s", |
|
930 |
pam_strerror(sshpam_handle, sshpam_err)); |
|
931 |
}
|
|
932 |
}
|
|
933 |
||
934 |
void
|
|
935 |
do_pam_setcred(int init) |
|
936 |
{
|
|
937 |
sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, |
|
1.1.2
by Colin Watson
Import upstream version 4.1p1 |
938 |
(const void *)&store_conv); |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
939 |
if (sshpam_err != PAM_SUCCESS) |
940 |
fatal("PAM: failed to set PAM_CONV: %s", |
|
941 |
pam_strerror(sshpam_handle, sshpam_err)); |
|
942 |
if (init) { |
|
943 |
debug("PAM: establishing credentials"); |
|
944 |
sshpam_err = pam_setcred(sshpam_handle, PAM_ESTABLISH_CRED); |
|
945 |
} else { |
|
946 |
debug("PAM: reinitializing credentials"); |
|
947 |
sshpam_err = pam_setcred(sshpam_handle, PAM_REINITIALIZE_CRED); |
|
948 |
}
|
|
949 |
if (sshpam_err == PAM_SUCCESS) { |
|
950 |
sshpam_cred_established = 1; |
|
951 |
return; |
|
952 |
}
|
|
953 |
if (sshpam_authenticated) |
|
954 |
fatal("PAM: pam_setcred(): %s", |
|
955 |
pam_strerror(sshpam_handle, sshpam_err)); |
|
956 |
else
|
|
957 |
debug("PAM: pam_setcred(): %s", |
|
958 |
pam_strerror(sshpam_handle, sshpam_err)); |
|
959 |
}
|
|
960 |
||
961 |
static int |
|
1.1.3
by Colin Watson
Import upstream version 4.2p1 |
962 |
sshpam_tty_conv(int n, sshpam_const struct pam_message **msg, |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
963 |
struct pam_response **resp, void *data) |
964 |
{
|
|
965 |
char input[PAM_MAX_MSG_SIZE]; |
|
966 |
struct pam_response *reply; |
|
967 |
int i; |
|
968 |
||
969 |
debug3("PAM: %s called with %d messages", __func__, n); |
|
970 |
||
971 |
*resp = NULL; |
|
972 |
||
973 |
if (n <= 0 || n > PAM_MAX_NUM_MSG || !isatty(STDIN_FILENO)) |
|
974 |
return (PAM_CONV_ERR); |
|
975 |
||
1.13.1
by Colin Watson
Import upstream version 4.6p1 |
976 |
if ((reply = calloc(n, sizeof(*reply))) == NULL) |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
977 |
return (PAM_CONV_ERR); |
978 |
||
979 |
for (i = 0; i < n; ++i) { |
|
980 |
switch (PAM_MSG_MEMBER(msg, i, msg_style)) { |
|
981 |
case PAM_PROMPT_ECHO_OFF: |
|
982 |
reply[i].resp = |
|
983 |
read_passphrase(PAM_MSG_MEMBER(msg, i, msg), |
|
984 |
RP_ALLOW_STDIN); |
|
985 |
reply[i].resp_retcode = PAM_SUCCESS; |
|
986 |
break; |
|
987 |
case PAM_PROMPT_ECHO_ON: |
|
988 |
fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg)); |
|
1.13.2
by Colin Watson
Import upstream version 4.7p1 |
989 |
if (fgets(input, sizeof input, stdin) == NULL) |
990 |
input[0] = '\0'; |
|
1.1.1
by Colin Watson
Import upstream version 3.9p1 |
991 |
if ((reply[i].resp = strdup(input)) == NULL) |
992 |
goto fail; |
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
993 |
reply[i].resp_retcode = PAM_SUCCESS; |
994 |
break; |
|
995 |
case PAM_ERROR_MSG: |
|
996 |
case PAM_TEXT_INFO: |
|
997 |
fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg)); |
|
998 |
reply[i].resp_retcode = PAM_SUCCESS; |
|
999 |
break; |
|
1000 |
default: |
|
1001 |
goto fail; |
|
1002 |
}
|
|
1003 |
}
|
|
1004 |
*resp = reply; |
|
1005 |
return (PAM_SUCCESS); |
|
1006 |
||
1007 |
fail: |
|
1008 |
for(i = 0; i < n; i++) { |
|
1009 |
if (reply[i].resp != NULL) |
|
1010 |
xfree(reply[i].resp); |
|
1011 |
}
|
|
1012 |
xfree(reply); |
|
1013 |
return (PAM_CONV_ERR); |
|
1014 |
}
|
|
1015 |
||
1.1.1
by Colin Watson
Import upstream version 3.9p1 |
1016 |
static struct pam_conv tty_conv = { sshpam_tty_conv, NULL }; |
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
1017 |
|
1018 |
/*
|
|
1019 |
* XXX this should be done in the authentication phase, but ssh1 doesn't
|
|
1020 |
* support that
|
|
1021 |
*/
|
|
1022 |
void
|
|
1023 |
do_pam_chauthtok(void) |
|
1024 |
{
|
|
1025 |
if (use_privsep) |
|
1026 |
fatal("Password expired (unable to change with privsep)"); |
|
1027 |
sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, |
|
1028 |
(const void *)&tty_conv); |
|
1029 |
if (sshpam_err != PAM_SUCCESS) |
|
1030 |
fatal("PAM: failed to set PAM_CONV: %s", |
|
1031 |
pam_strerror(sshpam_handle, sshpam_err)); |
|
1032 |
debug("PAM: changing password"); |
|
1033 |
sshpam_err = pam_chauthtok(sshpam_handle, PAM_CHANGE_EXPIRED_AUTHTOK); |
|
1034 |
if (sshpam_err != PAM_SUCCESS) |
|
1035 |
fatal("PAM: pam_chauthtok(): %s", |
|
1036 |
pam_strerror(sshpam_handle, sshpam_err)); |
|
1037 |
}
|
|
1038 |
||
1039 |
void
|
|
1040 |
do_pam_session(void) |
|
1041 |
{
|
|
1042 |
debug3("PAM: opening session"); |
|
1043 |
sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, |
|
1044 |
(const void *)&store_conv); |
|
1045 |
if (sshpam_err != PAM_SUCCESS) |
|
1046 |
fatal("PAM: failed to set PAM_CONV: %s", |
|
1047 |
pam_strerror(sshpam_handle, sshpam_err)); |
|
1048 |
sshpam_err = pam_open_session(sshpam_handle, 0); |
|
1.1.2
by Colin Watson
Import upstream version 4.1p1 |
1049 |
if (sshpam_err == PAM_SUCCESS) |
1050 |
sshpam_session_open = 1; |
|
1051 |
else { |
|
1052 |
sshpam_session_open = 0; |
|
1053 |
disable_forwarding(); |
|
1054 |
error("PAM: pam_open_session(): %s", |
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
1055 |
pam_strerror(sshpam_handle, sshpam_err)); |
1.1.2
by Colin Watson
Import upstream version 4.1p1 |
1056 |
}
|
1057 |
||
1058 |
}
|
|
1059 |
||
1060 |
int
|
|
1061 |
is_pam_session_open(void) |
|
1062 |
{
|
|
1063 |
return sshpam_session_open; |
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
1064 |
}
|
1065 |
||
1066 |
/*
|
|
1067 |
* Set a PAM environment string. We need to do this so that the session
|
|
1068 |
* modules can handle things like Kerberos/GSI credentials that appear
|
|
1069 |
* during the ssh authentication process.
|
|
1070 |
*/
|
|
1071 |
int
|
|
1072 |
do_pam_putenv(char *name, char *value) |
|
1073 |
{
|
|
1074 |
int ret = 1; |
|
1075 |
#ifdef HAVE_PAM_PUTENV
|
|
1076 |
char *compound; |
|
1077 |
size_t len; |
|
1078 |
||
1079 |
len = strlen(name) + strlen(value) + 2; |
|
1080 |
compound = xmalloc(len); |
|
1081 |
||
1082 |
snprintf(compound, len, "%s=%s", name, value); |
|
1083 |
ret = pam_putenv(sshpam_handle, compound); |
|
1084 |
xfree(compound); |
|
1085 |
#endif
|
|
1086 |
||
1087 |
return (ret); |
|
1088 |
}
|
|
1089 |
||
1090 |
char ** |
|
1091 |
fetch_pam_child_environment(void) |
|
1092 |
{
|
|
1093 |
return sshpam_env; |
|
1094 |
}
|
|
1095 |
||
1096 |
char ** |
|
1097 |
fetch_pam_environment(void) |
|
1098 |
{
|
|
1099 |
return (pam_getenvlist(sshpam_handle)); |
|
1100 |
}
|
|
1101 |
||
1102 |
void
|
|
1103 |
free_pam_environment(char **env) |
|
1104 |
{
|
|
1105 |
char **envp; |
|
1106 |
||
1107 |
if (env == NULL) |
|
1108 |
return; |
|
1109 |
||
1110 |
for (envp = env; *envp; envp++) |
|
1111 |
xfree(*envp); |
|
1112 |
xfree(env); |
|
1113 |
}
|
|
1114 |
||
1.1.1
by Colin Watson
Import upstream version 3.9p1 |
1115 |
/*
|
1116 |
* "Blind" conversation function for password authentication. Assumes that
|
|
1117 |
* echo-off prompts are for the password and stores messages for later
|
|
1118 |
* display.
|
|
1119 |
*/
|
|
1120 |
static int |
|
1.1.3
by Colin Watson
Import upstream version 4.2p1 |
1121 |
sshpam_passwd_conv(int n, sshpam_const struct pam_message **msg, |
1.1.1
by Colin Watson
Import upstream version 3.9p1 |
1122 |
struct pam_response **resp, void *data) |
1123 |
{
|
|
1124 |
struct pam_response *reply; |
|
1125 |
int i; |
|
1126 |
size_t len; |
|
1127 |
||
1128 |
debug3("PAM: %s called with %d messages", __func__, n); |
|
1129 |
||
1130 |
*resp = NULL; |
|
1131 |
||
1132 |
if (n <= 0 || n > PAM_MAX_NUM_MSG) |
|
1133 |
return (PAM_CONV_ERR); |
|
1134 |
||
1.13.2
by Colin Watson
Import upstream version 4.7p1 |
1135 |
if ((reply = calloc(n, sizeof(*reply))) == NULL) |
1.1.1
by Colin Watson
Import upstream version 3.9p1 |
1136 |
return (PAM_CONV_ERR); |
1137 |
||
1138 |
for (i = 0; i < n; ++i) { |
|
1139 |
switch (PAM_MSG_MEMBER(msg, i, msg_style)) { |
|
1140 |
case PAM_PROMPT_ECHO_OFF: |
|
1141 |
if (sshpam_password == NULL) |
|
1142 |
goto fail; |
|
1143 |
if ((reply[i].resp = strdup(sshpam_password)) == NULL) |
|
1144 |
goto fail; |
|
1145 |
reply[i].resp_retcode = PAM_SUCCESS; |
|
1146 |
break; |
|
1147 |
case PAM_ERROR_MSG: |
|
1148 |
case PAM_TEXT_INFO: |
|
1149 |
len = strlen(PAM_MSG_MEMBER(msg, i, msg)); |
|
1150 |
if (len > 0) { |
|
1151 |
buffer_append(&loginmsg, |
|
1152 |
PAM_MSG_MEMBER(msg, i, msg), len); |
|
1153 |
buffer_append(&loginmsg, "\n", 1); |
|
1154 |
}
|
|
1155 |
if ((reply[i].resp = strdup("")) == NULL) |
|
1156 |
goto fail; |
|
1157 |
reply[i].resp_retcode = PAM_SUCCESS; |
|
1158 |
break; |
|
1159 |
default: |
|
1160 |
goto fail; |
|
1161 |
}
|
|
1162 |
}
|
|
1163 |
*resp = reply; |
|
1164 |
return (PAM_SUCCESS); |
|
1165 |
||
1.1.3
by Colin Watson
Import upstream version 4.2p1 |
1166 |
fail: |
1.1.1
by Colin Watson
Import upstream version 3.9p1 |
1167 |
for(i = 0; i < n; i++) { |
1168 |
if (reply[i].resp != NULL) |
|
1169 |
xfree(reply[i].resp); |
|
1170 |
}
|
|
1171 |
xfree(reply); |
|
1172 |
return (PAM_CONV_ERR); |
|
1173 |
}
|
|
1174 |
||
1175 |
static struct pam_conv passwd_conv = { sshpam_passwd_conv, NULL }; |
|
1176 |
||
1177 |
/*
|
|
1178 |
* Attempt password authentication via PAM
|
|
1179 |
*/
|
|
1180 |
int
|
|
1181 |
sshpam_auth_passwd(Authctxt *authctxt, const char *password) |
|
1182 |
{
|
|
1183 |
int flags = (options.permit_empty_passwd == 0 ? |
|
1184 |
PAM_DISALLOW_NULL_AUTHTOK : 0); |
|
1185 |
||
1186 |
if (!options.use_pam || sshpam_handle == NULL) |
|
1187 |
fatal("PAM: %s called when PAM disabled or failed to " |
|
1188 |
"initialise.", __func__); |
|
1189 |
||
1190 |
sshpam_password = password; |
|
1191 |
sshpam_authctxt = authctxt; |
|
1192 |
||
1193 |
/*
|
|
1194 |
* If the user logging in is invalid, or is root but is not permitted
|
|
1195 |
* by PermitRootLogin, use an invalid password to prevent leaking
|
|
1196 |
* information via timing (eg if the PAM config has a delay on fail).
|
|
1197 |
*/
|
|
1198 |
if (!authctxt->valid || (authctxt->pw->pw_uid == 0 && |
|
1.1.3
by Colin Watson
Import upstream version 4.2p1 |
1199 |
options.permit_root_login != PERMIT_YES)) |
1.1.1
by Colin Watson
Import upstream version 3.9p1 |
1200 |
sshpam_password = badpw; |
1201 |
||
1202 |
sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, |
|
1203 |
(const void *)&passwd_conv); |
|
1204 |
if (sshpam_err != PAM_SUCCESS) |
|
1205 |
fatal("PAM: %s: failed to set PAM_CONV: %s", __func__, |
|
1206 |
pam_strerror(sshpam_handle, sshpam_err)); |
|
1207 |
||
1208 |
sshpam_err = pam_authenticate(sshpam_handle, flags); |
|
1209 |
sshpam_password = NULL; |
|
1210 |
if (sshpam_err == PAM_SUCCESS && authctxt->valid) { |
|
1211 |
debug("PAM: password authentication accepted for %.100s", |
|
1212 |
authctxt->user); |
|
1.1.3
by Colin Watson
Import upstream version 4.2p1 |
1213 |
return 1; |
1.1.1
by Colin Watson
Import upstream version 3.9p1 |
1214 |
} else { |
1215 |
debug("PAM: password authentication failed for %.100s: %s", |
|
1216 |
authctxt->valid ? authctxt->user : "an illegal user", |
|
1217 |
pam_strerror(sshpam_handle, sshpam_err)); |
|
1218 |
return 0; |
|
1219 |
}
|
|
1220 |
}
|
|
1
by Noah Meyerhans
Import upstream version 3.8.1p1 |
1221 |
#endif /* USE_PAM */ |