View | Details | Raw Unified | Return to bug 2675 | Differences between
and this patch

Collapse All | Expand All

(-)a/ssh-add.1 (-1 / +22 lines)
Lines 43-49 Link Here
43
.Nd adds private key identities to the authentication agent
43
.Nd adds private key identities to the authentication agent
44
.Sh SYNOPSIS
44
.Sh SYNOPSIS
45
.Nm ssh-add
45
.Nm ssh-add
46
.Op Fl cDdkLlqXx
46
.Op Fl cCDdfkLlqvXx
47
.Op Fl E Ar fingerprint_hash
47
.Op Fl E Ar fingerprint_hash
48
.Op Fl t Ar life
48
.Op Fl t Ar life
49
.Op Ar
49
.Op Ar
Lines 92-97 Confirmation is performed by Link Here
92
Successful confirmation is signaled by a zero exit status from
92
Successful confirmation is signaled by a zero exit status from
93
.Xr ssh-askpass 1 ,
93
.Xr ssh-askpass 1 ,
94
rather than text entered into the requester.
94
rather than text entered into the requester.
95
.It Fl C
96
When loading keys into or deleting keys from the agent, process certificate
97
keys only and plain keys.
95
.It Fl D
98
.It Fl D
96
Deletes all identities from the agent.
99
Deletes all identities from the agent.
97
.It Fl d
100
.It Fl d
Lines 118-123 The default is Link Here
118
.It Fl e Ar pkcs11
121
.It Fl e Ar pkcs11
119
Remove keys provided by the PKCS#11 shared library
122
Remove keys provided by the PKCS#11 shared library
120
.Ar pkcs11 .
123
.Ar pkcs11 .
124
.It Fl f
125
When loading certificate keys, disable expiry checking and automatic lifetime
126
limits.
127
By default,
128
.Nm
129
will refuse to load expired certificates and will request the agent to
130
automatically delete added certificates shortly after their validity
131
period expires.
121
.It Fl k
132
.It Fl k
122
When loading keys into or deleting keys from the agent, process plain private
133
When loading keys into or deleting keys from the agent, process plain private
123
keys only and skip certificates.
134
keys only and skip certificates.
Lines 136-141 Set a maximum lifetime when adding identities to an agent. Link Here
136
The lifetime may be specified in seconds or in a time format
147
The lifetime may be specified in seconds or in a time format
137
specified in
148
specified in
138
.Xr sshd_config 5 .
149
.Xr sshd_config 5 .
150
.It Fl v
151
Verbose mode.
152
Causes
153
.Nm
154
to print debugging messages about its progress.
155
This is helpful for debugging moduli generation.
156
Multiple
157
.Fl v
158
options increase the verbosity.
159
The maximum is 3.
139
.It Fl X
160
.It Fl X
140
Unlock the agent.
161
Unlock the agent.
141
.It Fl x
162
.It Fl x
(-)a/ssh-add.c (-54 / +145 lines)
Lines 75-80 static char *default_files[] = { Link Here
75
75
76
static int fingerprint_hash = SSH_FP_HASH_DEFAULT;
76
static int fingerprint_hash = SSH_FP_HASH_DEFAULT;
77
77
78
#define CERT_EXPIRY_GRACE	60	/* grace period for expired certs */
79
78
/* Default lifetime (0 == forever) */
80
/* Default lifetime (0 == forever) */
79
static int lifetime = 0;
81
static int lifetime = 0;
80
82
Lines 94-118 clear_pass(void) Link Here
94
}
96
}
95
97
96
static int
98
static int
97
delete_file(int agent_fd, const char *filename, int key_only, int qflag)
99
delete_file(int agent_fd, const char *filename, int key_only,
100
    int cert_only, int qflag)
98
{
101
{
99
	struct sshkey *public, *cert = NULL;
102
	struct sshkey *public, *cert = NULL;
100
	char *certpath = NULL, *comment = NULL;
103
	char *certpath = NULL, *comment = NULL;
101
	int r, ret = -1;
104
	int r, ret = -1;
102
105
103
	if ((r = sshkey_load_public(filename, &public,  &comment)) != 0) {
106
	if (!cert_only) {
104
		printf("Bad key file %s: %s\n", filename, ssh_err(r));
107
		if ((r = sshkey_load_public(filename, &public,
105
		return -1;
108
		    &comment)) != 0) {
106
	}
109
			printf("Bad key file %s: %s\n", filename, ssh_err(r));
107
	if ((r = ssh_remove_identity(agent_fd, public)) == 0) {
110
			return -1;
108
		if (!qflag) {
109
			fprintf(stderr, "Identity removed: %s (%s)\n",
110
			    filename, comment);
111
		}
111
		}
112
		ret = 0;
112
		if ((r = ssh_remove_identity(agent_fd, public)) == 0) {
113
	} else
113
			if (!qflag) {
114
		fprintf(stderr, "Could not remove identity \"%s\": %s\n",
114
				fprintf(stderr, "Identity removed: %s (%s)\n",
115
		    filename, ssh_err(r));
115
				    filename, comment);
116
			}
117
			ret = 0;
118
		} else
119
			fprintf(stderr, "Could not remove identity "
120
			    "\"%s\": %s\n", filename, ssh_err(r));
121
	}
116
122
117
	if (key_only)
123
	if (key_only)
118
		goto out;
124
		goto out;
Lines 175-188 delete_all(int agent_fd) Link Here
175
	return ret;
181
	return ret;
176
}
182
}
177
183
184
/* returns non-zero if certificate has expired */
178
static int
185
static int
179
add_file(int agent_fd, const char *filename, int key_only, int qflag)
186
cert_expired(const char *name, time_t now, const struct sshkey_cert *cert)
180
{
187
{
181
	struct sshkey *private, *cert;
188
	char msg[256];
189
190
	sshkey_format_cert_validity(cert, msg, sizeof(msg));
191
	if ((now < 0 || cert->valid_before < (u_int64_t)now)) {
192
		error("Certificate %s has expired, was valid %s", name, msg);
193
		return 1;
194
	}
195
	return 0;
196
}
197
198
static int
199
add_file(int agent_fd, const char *filename,
200
    int key_only, int cert_only, int force, int qflag)
201
{
202
	struct sshkey *private = NULL, *cert = NULL;
182
	char *comment = NULL;
203
	char *comment = NULL;
183
	char msg[1024], *certpath = NULL;
204
	char msg[1024], *certpath = NULL;
184
	int r, fd, ret = -1;
205
	int r, fd, ret = -1, cert_fail = 0;
185
	struct sshbuf *keyblob;
206
	struct sshbuf *keyblob;
207
	time_t now;
208
	u_int64_t cert_lifetime;
186
209
187
	if (strcmp(filename, "-") == 0) {
210
	if (strcmp(filename, "-") == 0) {
188
		fd = STDIN_FILENO;
211
		fd = STDIN_FILENO;
Lines 213-218 add_file(int agent_fd, const char *filename, int key_only, int qflag) Link Here
213
	}
236
	}
214
	close(fd);
237
	close(fd);
215
238
239
	/* Load and check the certificate before unwrapping private key */
240
	if (!key_only) {
241
		xasprintf(&certpath, "%s-cert.pub", filename);
242
		if ((r = sshkey_load_public(certpath, &cert, NULL)) != 0) {
243
			if (r != SSH_ERR_SYSTEM_ERROR || errno != ENOENT)
244
				error("Failed to load certificate \"%s\": %s",
245
				    certpath, ssh_err(r));
246
			cert_fail = 1;
247
		} else {
248
			sshkey_format_cert_validity(cert->cert,
249
			    msg, sizeof(msg));
250
			debug("Certificate %s valid %s", certpath, msg);
251
			/* Skip expired certificates */
252
			if (!force &&
253
			    cert_expired(certpath, time(NULL), cert->cert)) {
254
				cert_fail = 1;
255
			}
256
		}
257
	}
258
	if (cert_fail && cert_only)
259
		goto out;
260
216
	/* At first, try empty passphrase */
261
	/* At first, try empty passphrase */
217
	if ((r = sshkey_parse_private_fileblob(keyblob, "", &private,
262
	if ((r = sshkey_parse_private_fileblob(keyblob, "", &private,
218
	    &comment)) != 0 && r != SSH_ERR_KEY_WRONG_PASSPHRASE) {
263
	    &comment)) != 0 && r != SSH_ERR_KEY_WRONG_PASSPHRASE) {
Lines 260-301 add_file(int agent_fd, const char *filename, int key_only, int qflag) Link Here
260
		comment = xstrdup(filename);
305
		comment = xstrdup(filename);
261
	sshbuf_free(keyblob);
306
	sshbuf_free(keyblob);
262
307
263
	if ((r = ssh_add_identity_constrained(agent_fd, private, comment,
308
	if (!cert_only) {
264
	    lifetime, confirm)) == 0) {
309
		if ((r = ssh_add_identity_constrained(agent_fd,
265
		fprintf(stderr, "Identity added: %s (%s)\n", filename, comment);
310
		    private, comment, lifetime, confirm)) == 0) {
266
		ret = 0;
311
			fprintf(stderr, "Identity added: %s (%s)\n",
267
		if (lifetime != 0)
312
			    filename, comment);
268
			fprintf(stderr,
313
			ret = 0;
269
			    "Lifetime set to %d seconds\n", lifetime);
314
			if (lifetime != 0)
270
		if (confirm != 0)
315
				fprintf(stderr,
271
			fprintf(stderr,
316
				    "Lifetime set to %d seconds\n", lifetime);
272
			    "The user must confirm each use of the key\n");
317
			if (confirm != 0)
273
	} else {
318
				fprintf(stderr, "The user must confirm each "
274
		fprintf(stderr, "Could not add identity \"%s\": %s\n",
319
				    "use of the key\n");
275
		    filename, ssh_err(r));
320
		} else {
321
			fprintf(stderr, "Could not add identity \"%s\": %s\n",
322
			    filename, ssh_err(r));
323
		}
276
	}
324
	}
277
325
278
	/* Skip trying to load the cert if requested */
326
	/* Skip trying to load the cert if requested */
279
	if (key_only)
327
	if (key_only || cert_fail)
280
		goto out;
328
		goto out;
281
329
282
	/* Now try to add the certificate flavour too */
283
	xasprintf(&certpath, "%s-cert.pub", filename);
284
	if ((r = sshkey_load_public(certpath, &cert, NULL)) != 0) {
285
		if (r != SSH_ERR_SYSTEM_ERROR || errno != ENOENT)
286
			error("Failed to load certificate \"%s\": %s",
287
			    certpath, ssh_err(r));
288
		goto out;
289
	}
290
291
	if (!sshkey_equal_public(cert, private)) {
330
	if (!sshkey_equal_public(cert, private)) {
292
		error("Certificate %s does not match private key %s",
331
		error("Certificate %s does not match private key %s",
293
		    certpath, filename);
332
		    certpath, filename);
294
		sshkey_free(cert);
333
		sshkey_free(cert);
295
		goto out;
334
		goto out;
296
	} 
335
	}
297
336
298
	/* Graft with private bits */
337
	/* Redo expiry check in case user lingered at passphrase prompt */
338
	now = time(NULL);
339
	if (!force && cert_expired(certpath, now, cert->cert))
340
		goto out;
341
342
	/* Clamp agent lifetimes to cert lifetime plus some grace */
343
	cert_lifetime = cert->cert->valid_before - (u_int64_t)now;
344
	if (cert->cert->valid_before == ~(u_int64_t)0)
345
		debug("Certificate %s has no expiry time", certpath);
346
	else
347
		debug("Certificate %s has %llu seconds remaining",
348
		    certpath, cert_lifetime);
349
	if (cert_lifetime < (u_int64_t)0 - CERT_EXPIRY_GRACE)
350
		cert_lifetime += CERT_EXPIRY_GRACE;
351
	/*
352
	 * Don't fiddle lifetimes if the cert didn't have an expiry date,
353
	 * the lifetime would be greater than that which could be expressed
354
	 * on the wire, less than the lifetimes set by the user, or the -f
355
	 * (force) flag was set.
356
	 */
357
	if (force || cert->cert->valid_before == ~(u_int64_t)0 ||
358
	    cert_lifetime > UINT_MAX ||
359
	    (lifetime != 0 && (u_int64_t)lifetime < cert_lifetime))
360
		cert_lifetime = lifetime;
361
362
	/* Graft certificate to private bits */
299
	if ((r = sshkey_to_certified(private)) != 0) {
363
	if ((r = sshkey_to_certified(private)) != 0) {
300
		error("%s: sshkey_to_certified: %s", __func__, ssh_err(r));
364
		error("%s: sshkey_to_certified: %s", __func__, ssh_err(r));
301
		sshkey_free(cert);
365
		sshkey_free(cert);
Lines 309-323 add_file(int agent_fd, const char *filename, int key_only, int qflag) Link Here
309
	sshkey_free(cert);
373
	sshkey_free(cert);
310
374
311
	if ((r = ssh_add_identity_constrained(agent_fd, private, comment,
375
	if ((r = ssh_add_identity_constrained(agent_fd, private, comment,
312
	    lifetime, confirm)) != 0) {
376
	    cert_lifetime, confirm)) != 0) {
313
		error("Certificate %s (%s) add failed: %s", certpath,
377
		error("Certificate %s (%s) add failed: %s", certpath,
314
		    private->cert->key_id, ssh_err(r));
378
		    private->cert->key_id, ssh_err(r));
315
		goto out;
379
		goto out;
316
	}
380
	}
317
	fprintf(stderr, "Certificate added: %s (%s)\n", certpath,
381
	fprintf(stderr, "Certificate added: %s (%s)\n", certpath,
318
	    private->cert->key_id);
382
	    private->cert->key_id);
319
	if (lifetime != 0)
383
	if (cert_lifetime != 0)
320
		fprintf(stderr, "Lifetime set to %d seconds\n", lifetime);
384
		fprintf(stderr, "Lifetime set to %llu seconds\n",
385
		    cert_lifetime);
321
	if (confirm != 0)
386
	if (confirm != 0)
322
		fprintf(stderr, "The user must confirm each use of the key\n");
387
		fprintf(stderr, "The user must confirm each use of the key\n");
323
 out:
388
 out:
Lines 424-436 lock_agent(int agent_fd, int lock) Link Here
424
}
489
}
425
490
426
static int
491
static int
427
do_file(int agent_fd, int deleting, int key_only, char *file, int qflag)
492
do_file(int agent_fd, int deleting, int key_only, int cert_only,
493
    int force, char *file, int qflag)
428
{
494
{
429
	if (deleting) {
495
	if (deleting) {
430
		if (delete_file(agent_fd, file, key_only, qflag) == -1)
496
		if (delete_file(agent_fd, file, key_only,
497
		    cert_only, qflag) == -1)
431
			return -1;
498
			return -1;
432
	} else {
499
	} else {
433
		if (add_file(agent_fd, file, key_only, qflag) == -1)
500
		if (add_file(agent_fd, file, key_only, cert_only,
501
		    force, qflag) == -1)
434
			return -1;
502
			return -1;
435
	}
503
	}
436
	return 0;
504
	return 0;
Lines 463-470 main(int argc, char **argv) Link Here
463
	extern int optind;
531
	extern int optind;
464
	int agent_fd;
532
	int agent_fd;
465
	char *pkcs11provider = NULL;
533
	char *pkcs11provider = NULL;
466
	int r, i, ch, deleting = 0, ret = 0, key_only = 0;
534
	int r, i, ch, ret = 0;
535
	int deleting = 0, key_only = 0, cert_only = 0, force = 0;
467
	int xflag = 0, lflag = 0, Dflag = 0, qflag = 0;
536
	int xflag = 0, lflag = 0, Dflag = 0, qflag = 0;
537
	int log_level = SYSLOG_LEVEL_INFO;
468
538
469
	ssh_malloc_init();	/* must be called before any mallocs */
539
	ssh_malloc_init();	/* must be called before any mallocs */
470
	/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
540
	/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
Lines 474-479 main(int argc, char **argv) Link Here
474
544
475
	setvbuf(stdout, NULL, _IOLBF, 0);
545
	setvbuf(stdout, NULL, _IOLBF, 0);
476
546
547
	log_init(argv[0], SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_USER, 1);
548
477
	/* First, get a connection to the authentication agent. */
549
	/* First, get a connection to the authentication agent. */
478
	switch (r = ssh_get_authentication_socket(&agent_fd)) {
550
	switch (r = ssh_get_authentication_socket(&agent_fd)) {
479
	case 0:
551
	case 0:
Lines 487-499 main(int argc, char **argv) Link Here
487
		exit(2);
559
		exit(2);
488
	}
560
	}
489
561
490
	while ((ch = getopt(argc, argv, "klLcdDxXE:e:qs:t:")) != -1) {
562
	while ((ch = getopt(argc, argv, "CcDdfkLlqvXxE:e:s:t:")) != -1) {
491
		switch (ch) {
563
		switch (ch) {
564
		case 'C':
565
			cert_only = 1;
566
			break;
492
		case 'E':
567
		case 'E':
493
			fingerprint_hash = ssh_digest_alg_by_name(optarg);
568
			fingerprint_hash = ssh_digest_alg_by_name(optarg);
494
			if (fingerprint_hash == -1)
569
			if (fingerprint_hash == -1)
495
				fatal("Invalid hash algorithm \"%s\"", optarg);
570
				fatal("Invalid hash algorithm \"%s\"", optarg);
496
			break;
571
			break;
572
		case 'f':
573
			force = 1;
574
			break;
497
		case 'k':
575
		case 'k':
498
			key_only = 1;
576
			key_only = 1;
499
			break;
577
			break;
Lines 535-540 main(int argc, char **argv) Link Here
535
		case 'q':
613
		case 'q':
536
			qflag = 1;
614
			qflag = 1;
537
			break;
615
			break;
616
		case 'v':
617
			if (log_level == SYSLOG_LEVEL_INFO)
618
				log_level = SYSLOG_LEVEL_DEBUG1;
619
			else {
620
				if (log_level >= SYSLOG_LEVEL_DEBUG1 &&
621
				    log_level < SYSLOG_LEVEL_DEBUG3)
622
					log_level++;
623
			}
624
			break;
538
		default:
625
		default:
539
			usage();
626
			usage();
540
			ret = 1;
627
			ret = 1;
Lines 542-548 main(int argc, char **argv) Link Here
542
		}
629
		}
543
	}
630
	}
544
631
545
	if ((xflag != 0) + (lflag != 0) + (Dflag != 0) > 1)
632
	/* reinit */
633
	log_init(argv[0], log_level, SYSLOG_FACILITY_USER, 1);
634
635
	if ((xflag != 0) + (lflag != 0) + (Dflag != 0) > 1 ||
636
	    (key_only && cert_only))
546
		fatal("Invalid combination of actions");
637
		fatal("Invalid combination of actions");
547
	else if (xflag) {
638
	else if (xflag) {
548
		if (lock_agent(agent_fd, xflag == 'x' ? 1 : 0) == -1)
639
		if (lock_agent(agent_fd, xflag == 'x' ? 1 : 0) == -1)
Lines 583-590 main(int argc, char **argv) Link Here
583
			    default_files[i]);
674
			    default_files[i]);
584
			if (stat(buf, &st) < 0)
675
			if (stat(buf, &st) < 0)
585
				continue;
676
				continue;
586
			if (do_file(agent_fd, deleting, key_only, buf,
677
			if (do_file(agent_fd, deleting, key_only, cert_only,
587
			    qflag) == -1)
678
			    force, buf, qflag) == -1)
588
				ret = 1;
679
				ret = 1;
589
			else
680
			else
590
				count++;
681
				count++;
Lines 593-600 main(int argc, char **argv) Link Here
593
			ret = 1;
684
			ret = 1;
594
	} else {
685
	} else {
595
		for (i = 0; i < argc; i++) {
686
		for (i = 0; i < argc; i++) {
596
			if (do_file(agent_fd, deleting, key_only,
687
			if (do_file(agent_fd, deleting, key_only, cert_only,
597
			    argv[i], qflag) == -1)
688
			    force, argv[i], qflag) == -1)
598
				ret = 1;
689
				ret = 1;
599
		}
690
		}
600
	}
691
	}

Return to bug 2675