View | Details | Raw Unified | Return to bug 21877
Collapse All | Expand All

(-)pam_ssh.c (-93 / +175 lines)
Lines 28-38 Link Here
28
 */
28
 */
29
29
30
30
31
#include <sys/param.h>
32
#include <sys/queue.h>
31
#include <sys/queue.h>
32
#include <sys/stat.h>
33
33
34
#include <fcntl.h>
35
#include <paths.h>
36
#include <pwd.h>
34
#include <pwd.h>
37
#include <stdio.h>
35
#include <stdio.h>
38
#include <stdlib.h>
36
#include <stdlib.h>
Lines 45-50 Link Here
45
#include <security/pam_mod_misc.h>
43
#include <security/pam_mod_misc.h>
46
44
47
#include <openssl/dsa.h>
45
#include <openssl/dsa.h>
46
#include <openssl/evp.h>
48
47
49
#include "includes.h"
48
#include "includes.h"
50
#include "rsa.h"
49
#include "rsa.h"
Lines 54-71 Link Here
54
#include "authfile.h"
53
#include "authfile.h"
55
54
56
#define	MODULE_NAME	"pam_ssh"
55
#define	MODULE_NAME	"pam_ssh"
57
#define	NEED_PASSPHRASE	"Need passphrase for %s (%s).\nEnter passphrase: "
56
#define	NEED_PASSPHRASE	"SSH passphrase: "
58
#define	PATH_SSH_AGENT	"/usr/bin/ssh-agent"
57
#define	PATH_SSH_AGENT	"/usr/bin/ssh-agent"
59
58
60
59
60
/*
61
 * Generic cleanup function for SSH "Key" type.
62
 */
63
61
void
64
void
62
rsa_cleanup(pam_handle_t *pamh, void *data, int error_status)
65
key_cleanup(pam_handle_t *pamh, void *data, int error_status)
63
{
66
{
64
	if (data)
67
	if (data)
65
		RSA_free(data);
68
		key_free(data);
66
}
69
}
67
70
68
71
72
/*
73
 * Generic PAM cleanup function for this module.
74
 */
75
69
void
76
void
70
ssh_cleanup(pam_handle_t *pamh, void *data, int error_status)
77
ssh_cleanup(pam_handle_t *pamh, void *data, int error_status)
71
{
78
{
Lines 107-112 Link Here
107
extern char **environ;
114
extern char **environ;
108
115
109
116
117
/*
118
 * Create a new environment list.
119
 */
120
110
static ENV *
121
static ENV *
111
env_new(void)
122
env_new(void)
112
{
123
{
Lines 123-130 Link Here
123
}
134
}
124
135
125
136
137
/*
138
 * Insert a new entry into the list.
139
 */
140
126
static int
141
static int
127
env_put(ENV *self, char *s)
142
env_put(ENV *self, const char *s)
128
{
143
{
129
	struct env_entry	*env;
144
	struct env_entry	*env;
130
145
Lines 139-151 Link Here
139
}
154
}
140
155
141
156
157
/*
158
 * Switch between the original environ string and our crafted one.
159
 */
160
142
static void
161
static void
143
env_swap(ENV *self, int which)
162
env_swap(const ENV *self, int which)
144
{
163
{
145
	environ = which ? self->e_environ_new : self->e_environ_orig;
164
	environ = which ? self->e_environ_new : self->e_environ_orig;
146
}
165
}
147
166
148
167
168
/*
169
 * Craft an environ string out of the list we've built.
170
 */
171
149
static int
172
static int
150
env_commit(ENV *self)
173
env_commit(ENV *self)
151
{
174
{
Lines 171-185 Link Here
171
}
194
}
172
195
173
196
197
/*
198
 * Destroy our list and environ string.
199
 */
200
174
static void
201
static void
175
env_destroy(ENV *self)
202
env_destroy(ENV *self)
176
{
203
{
177
	struct env_entry	 *p;
204
	struct env_entry	 *p;
178
205
179
	env_swap(self, 0);
206
	env_swap(self, 0);
180
	SLIST_FOREACH(p, &self->e_head, ee_entries) {
207
	while ((p = SLIST_FIRST(&self->e_head))) {
181
		free(p->ee_env);
208
		free(p->ee_env);
182
		free(p);
209
		free(p);
210
		SLIST_REMOVE_HEAD(&self->e_head, ee_entries);
183
	}
211
	}
184
	if (self->e_committed)
212
	if (self->e_committed)
185
		free(self->e_environ_new);
213
		free(self->e_environ_new);
Lines 187-192 Link Here
187
}
215
}
188
216
189
217
218
/*
219
 * Cleanup function for PAM data storage.
220
 */
221
190
void
222
void
191
env_cleanup(pam_handle_t *pamh, void *data, int error_status)
223
env_cleanup(pam_handle_t *pamh, void *data, int error_status)
192
{
224
{
Lines 195-200 Link Here
195
}
227
}
196
228
197
229
230
/*
231
 * Authenticate a user's key by trying to decrypt it with the password
232
 * provided.  The key and its comment are then stored for later
233
 * retrieval by the session phase.  An increasing index is embedded in
234
 * the PAM variable names so this function may be called multiple times
235
 * for multiple keys.
236
 */
237
238
int
239
auth_via_key(
240
	pam_handle_t		*pamh,
241
	int			 type,
242
	const char		*file,
243
	const struct passwd	*user,
244
	const char		*pass
245
)
246
{
247
	char		*comment;		/* private key comment */
248
	char		*data_name;		/* PAM state */
249
	static int	 index = 0;		/* for saved keys */
250
	Key		*key;			/* user's key */
251
	char		*path;			/* to key files */
252
	int		 retval;		/* from calls */
253
	uid_t		 saved_uid;		/* caller's uid */
254
255
	/* locate the user's private key file */
256
	if (!asprintf(&path, "%s/%s", user->pw_dir, file)) {
257
		syslog(LOG_CRIT, "%s: %m", MODULE_NAME);
258
		return PAM_SERVICE_ERR;
259
	}
260
	saved_uid = getuid();
261
	/*
262
	 * Try to decrypt the private key with the passphrase provided.
263
	 * If success, the user is authenticated.
264
	 */
265
	key = key_new(type);
266
	(void)setreuid(user->pw_uid, saved_uid);
267
	retval = load_private_key(path, pass, key, &comment);
268
	free(path);
269
	(void)setuid(saved_uid);
270
	if (!retval)
271
		return retval;
272
	/*
273
	 * Save the key and comment to pass to ssh-agent in the session
274
	 * phase.
275
	 */
276
	if (!asprintf(&data_name, "ssh_private_key_%d", index)) {
277
		syslog(LOG_CRIT, "%s: %m", MODULE_NAME);
278
		free(comment);
279
		return PAM_SERVICE_ERR;
280
	}
281
	retval = pam_set_data(pamh, data_name, key, key_cleanup);
282
	free(data_name);
283
	if (retval != PAM_SUCCESS) {
284
		key_free(key);
285
		free(comment);
286
		return retval;
287
	}
288
	if (!asprintf(&data_name, "ssh_key_comment_%d", index)) {
289
		syslog(LOG_CRIT, "%s: %m", MODULE_NAME);
290
		free(comment);
291
		return PAM_SERVICE_ERR;
292
	}
293
	retval = pam_set_data(pamh, data_name, comment, ssh_cleanup);
294
	free(data_name);
295
	if (retval != PAM_SUCCESS) {
296
		free(comment);
297
		return retval;
298
	}
299
	++index;
300
	return PAM_SUCCESS;
301
}
302
303
198
typedef struct passwd PASSWD;
304
typedef struct passwd PASSWD;
199
305
200
PAM_EXTERN int
306
PAM_EXTERN int
Lines 204-221 Link Here
204
	int		  argc,
310
	int		  argc,
205
	const char	**argv)
311
	const char	**argv)
206
{
312
{
207
	char		*comment_priv;		/* on private key */
208
	char		*comment_pub;		/* on public key */
209
	char		*identity;		/* user's identity file */
210
	Key		 key;			/* user's private key */
211
	int		 options;		/* module options */
313
	int		 options;		/* module options */
212
	const char	*pass;			/* passphrase */
314
	const char	*pass;			/* passphrase */
213
	char		*prompt;		/* passphrase prompt */
214
	Key		 public_key;		/* user's public key */
215
	const PASSWD	*pwent;			/* user's passwd entry */
315
	const PASSWD	*pwent;			/* user's passwd entry */
216
	PASSWD		*pwent_keep;		/* our own copy */
316
	PASSWD		*pwent_keep;		/* our own copy */
217
	int		 retval;		/* from calls */
317
	int		 retval;		/* from calls */
218
	uid_t		 saved_uid;		/* caller's uid */
219
	const char	*user;			/* username */
318
	const char	*user;			/* username */
220
319
221
	options = 0;
320
	options = 0;
Lines 227-296 Link Here
227
		/* delay? */
326
		/* delay? */
228
		return PAM_AUTH_ERR;
327
		return PAM_AUTH_ERR;
229
	}
328
	}
230
	/* locate the user's private key file */
231
	if (!asprintf(&identity, "%s/%s", pwent->pw_dir,
232
	    SSH_CLIENT_IDENTITY)) {
233
		syslog(LOG_CRIT, "%s: %m", MODULE_NAME);
234
		return PAM_SERVICE_ERR;
235
	}
236
	/*
329
	/*
237
	 * Fail unless we can load the public key.  Change to the
330
	 * Pass prompt message to application and receive
238
	 * owner's UID to appease load_public_key().
331
	 * passphrase.
239
	 */
332
	 */
240
	key.type = KEY_RSA;
333
	if ((retval = pam_get_pass(pamh, &pass, NEED_PASSPHRASE, options))
241
	key.rsa = RSA_new();
334
	    != PAM_SUCCESS)
242
	public_key.type = KEY_RSA;
243
	public_key.rsa = RSA_new();
244
	saved_uid = getuid();
245
	(void)setreuid(pwent->pw_uid, saved_uid);
246
	retval = load_public_key(identity, &public_key, &comment_pub);
247
	(void)setuid(saved_uid);
248
	if (!retval) {
249
		free(identity);
250
		return PAM_AUTH_ERR;
251
	}
252
	RSA_free(public_key.rsa);
253
	/* build the passphrase prompt */
254
	retval = asprintf(&prompt, NEED_PASSPHRASE, identity, comment_pub);
255
	free(comment_pub);
256
	if (!retval) {
257
		syslog(LOG_CRIT, "%s: %m", MODULE_NAME);
258
		free(identity);
259
		return PAM_SERVICE_ERR;
260
	}
261
	/* pass prompt message to application and receive passphrase */
262
	retval = pam_get_pass(pamh, &pass, prompt, options);
263
	free(prompt);
264
	if (retval != PAM_SUCCESS) {
265
		free(identity);
266
		return retval;
335
		return retval;
267
	}
336
	OpenSSL_add_all_algorithms();	/* required for DSA */
268
	/*
337
	/*
269
	 * Try to decrypt the private key with the passphrase provided.
338
	 * Either the DSA or the RSA key will authenticate us, but if
270
	 * If success, the user is authenticated.
339
	 * we can decrypt both, we'll do so here so we can cache them in
340
	 * the session phase.
271
	 */
341
	 */
272
	(void)setreuid(pwent->pw_uid, saved_uid);
342
	retval = auth_via_key(pamh, KEY_DSA, SSH_CLIENT_ID_DSA, pwent,
273
	retval = load_private_key(identity, pass, &key, &comment_priv);
343
	    pass);
274
	free(identity);
344
	if (auth_via_key(pamh, KEY_RSA, SSH_CLIENT_IDENTITY, pwent, pass)
275
	(void)setuid(saved_uid);
345
	    != PAM_SUCCESS && retval != PAM_SUCCESS)
276
	if (!retval)
277
		return PAM_AUTH_ERR;
346
		return PAM_AUTH_ERR;
278
	/*
347
	/*
279
	 * Save the key and comment to pass to ssh-agent in the session
280
	 * phase.
281
	 */
282
	if ((retval = pam_set_data(pamh, "ssh_private_key", key.rsa,
283
	    rsa_cleanup)) != PAM_SUCCESS) {
284
		RSA_free(key.rsa);
285
		free(comment_priv);
286
		return retval;
287
	}
288
	if ((retval = pam_set_data(pamh, "ssh_key_comment", comment_priv,
289
	    ssh_cleanup)) != PAM_SUCCESS) {
290
		free(comment_priv);
291
		return retval;
292
	}
293
	/*
294
	 * Copy the passwd entry (in case successive calls are made)
348
	 * Copy the passwd entry (in case successive calls are made)
295
	 * and save it for the session phase.
349
	 * and save it for the session phase.
296
	 */
350
	 */
Lines 333-339 Link Here
333
	char		*env_end;		/* end of env */
387
	char		*env_end;		/* end of env */
334
	char		*env_file;		/* to store env */
388
	char		*env_file;		/* to store env */
335
	FILE		*env_fp;		/* env_file handle */
389
	FILE		*env_fp;		/* env_file handle */
336
	Key		 key;			/* user's private key */
390
	char		*data_name;		/* PAM state */
391
	int		 final;			/* final return value */
392
	int		 index;			/* for saved keys */
393
	Key		*key;			/* user's private key */
337
	FILE		*pipe;			/* ssh-agent handle */
394
	FILE		*pipe;			/* ssh-agent handle */
338
	const PASSWD	*pwent;			/* user's passwd entry */
395
	const PASSWD	*pwent;			/* user's passwd entry */
339
	int		 retval;		/* from calls */
396
	int		 retval;		/* from calls */
Lines 351-359 Link Here
351
	if ((retval = pam_get_item(pamh, PAM_TTY, (const void **)&tty))
408
	if ((retval = pam_get_item(pamh, PAM_TTY, (const void **)&tty))
352
	    != PAM_SUCCESS)
409
	    != PAM_SUCCESS)
353
		return retval;
410
		return retval;
354
	if (*tty == ':' && gethostname(hname, sizeof hname) == 0) {
411
	if (gethostname(hname, sizeof hname) == 0) {
355
		if (asprintf(&env_file, "%s/.ssh/agent-%s%s",
412
		if (asprintf(&env_file, "%s/.ssh/agent-%s%s%s",
356
		    pwent->pw_dir, hname, tty) == -1) {
413
		    pwent->pw_dir, hname, *tty == ':' ? "" : ":", tty)
414
		    == -1) {
357
			syslog(LOG_CRIT, "%s: %m", MODULE_NAME);
415
			syslog(LOG_CRIT, "%s: %m", MODULE_NAME);
358
			return PAM_SERVICE_ERR;
416
			return PAM_SERVICE_ERR;
359
		}
417
		}
Lines 371-377 Link Here
371
	/* start the agent as the user */
429
	/* start the agent as the user */
372
	saved_uid = geteuid();
430
	saved_uid = geteuid();
373
	(void)seteuid(pwent->pw_uid);
431
	(void)seteuid(pwent->pw_uid);
374
	env_fp = fopen(env_file, "w");
432
	if ((env_fp = fopen(env_file, "w")))
433
		(void)chmod(env_file, S_IRUSR);
375
	pipe = popen(PATH_SSH_AGENT, "r");
434
	pipe = popen(PATH_SSH_AGENT, "r");
376
	(void)seteuid(saved_uid);
435
	(void)seteuid(saved_uid);
377
	if (!pipe) {
436
	if (!pipe) {
Lines 380-385 Link Here
380
			(void)fclose(env_fp);
439
			(void)fclose(env_fp);
381
		return PAM_SESSION_ERR;
440
		return PAM_SESSION_ERR;
382
	}
441
	}
442
	/*
443
	 * Save environment for application with pam_putenv()
444
	 * but also with env_* functions for our own call to
445
	 * ssh_get_authentication_connection().
446
	 */
383
	if (!(ssh_env = env_new()))
447
	if (!(ssh_env = env_new()))
384
		return PAM_SESSION_ERR;
448
		return PAM_SESSION_ERR;
385
	if ((retval = pam_set_data(pamh, "ssh_env_handle", ssh_env,
449
	if ((retval = pam_set_data(pamh, "ssh_env_handle", ssh_env,
Lines 388-398 Link Here
388
	while (fgets(parse, sizeof parse, pipe)) {
452
	while (fgets(parse, sizeof parse, pipe)) {
389
		if (env_fp)
453
		if (env_fp)
390
			(void)fputs(parse, env_fp);
454
			(void)fputs(parse, env_fp);
391
		/*
392
		 * Save environment for application with pam_putenv()
393
		 * but also with env_* functions for our own call to
394
		 * ssh_get_authentication_connection().
395
		 */
396
		if (strchr(parse, '=') && (env_end = strchr(parse, ';'))) {
455
		if (strchr(parse, '=') && (env_end = strchr(parse, ';'))) {
397
			*env_end = '\0';
456
			*env_end = '\0';
398
			/* pass to the application ... */
457
			/* pass to the application ... */
Lines 427-439 Link Here
427
		env_destroy(ssh_env);
486
		env_destroy(ssh_env);
428
		return PAM_SESSION_ERR;
487
		return PAM_SESSION_ERR;
429
	}
488
	}
430
	key.type = KEY_RSA;
489
	/* connect to the agent */
431
	/* connect to the agent and hand off the private key */
490
	if ((retval = env_commit(ssh_env)) != PAM_SUCCESS) {
432
	if ((retval = pam_get_data(pamh, "ssh_private_key",
433
	    (const void **)&key.rsa)) != PAM_SUCCESS ||
434
	    (retval = pam_get_data(pamh, "ssh_key_comment",
435
	    (const void **)&comment)) != PAM_SUCCESS ||
436
	    (retval = env_commit(ssh_env)) != PAM_SUCCESS) {
437
		env_destroy(ssh_env);
491
		env_destroy(ssh_env);
438
		return retval;
492
		return retval;
439
	}
493
	}
Lines 442-452 Link Here
442
		    MODULE_NAME);
496
		    MODULE_NAME);
443
		env_destroy(ssh_env);
497
		env_destroy(ssh_env);
444
		return PAM_SESSION_ERR;
498
		return PAM_SESSION_ERR;
499
	}
500
	/* hand off each private key to the agent */
501
	final = 0;
502
	for (index = 0; ; index++) {
503
		if (!asprintf(&data_name, "ssh_private_key_%d", index)) {
504
			syslog(LOG_CRIT, "%s: %m", MODULE_NAME);
505
			ssh_close_authentication_connection(ac);
506
			env_destroy(ssh_env);
507
			return PAM_SERVICE_ERR;
508
		}
509
		retval = pam_get_data(pamh, data_name, (const void **)&key);
510
		free(data_name);
511
		if (retval != PAM_SUCCESS)
512
			break;
513
		if (!asprintf(&data_name, "ssh_key_comment_%d", index)) {
514
			syslog(LOG_CRIT, "%s: %m", MODULE_NAME);
515
			ssh_close_authentication_connection(ac);
516
			env_destroy(ssh_env);
517
			return PAM_SERVICE_ERR;
518
		}
519
		retval = pam_get_data(pamh, data_name,
520
		    (const void **)&comment);
521
		free(data_name);
522
		if (retval != PAM_SUCCESS)
523
			break;
524
		retval = ssh_add_identity(ac, key, comment);
525
		if (!final)
526
			final = retval;
445
	}
527
	}
446
	retval = ssh_add_identity(ac, key.rsa, comment);
447
	ssh_close_authentication_connection(ac);
528
	ssh_close_authentication_connection(ac);
448
	env_swap(ssh_env, 0);
529
	env_swap(ssh_env, 0);		/* restore original environment */
449
	return retval ? PAM_SUCCESS : PAM_SESSION_ERR;
530
	return final ? PAM_SUCCESS : PAM_SESSION_ERR;
450
}
531
}
451
532
452
533
Lines 461-466 Link Here
461
	int	 	 retval;	/* from calls */
542
	int	 	 retval;	/* from calls */
462
	ENV		*ssh_env;	/* env handle */
543
	ENV		*ssh_env;	/* env handle */
463
544
545
	/* invoke the cached environment to re-access the agent */
464
	if ((retval = pam_get_data(pamh, "ssh_env_handle",
546
	if ((retval = pam_get_data(pamh, "ssh_env_handle",
465
	    (const void **)&ssh_env)) != PAM_SUCCESS)
547
	    (const void **)&ssh_env)) != PAM_SUCCESS)
466
		return retval;
548
		return retval;

Return to bug 21877