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

Collapse All | Expand All

(-)a/auth2-pubkey.c (-125 / +411 lines)
Lines 62-67 Link Here
62
#include "monitor_wrap.h"
62
#include "monitor_wrap.h"
63
#include "authfile.h"
63
#include "authfile.h"
64
#include "match.h"
64
#include "match.h"
65
#include "ssherr.h"
66
#include "channels.h" /* XXX for session.h */
67
#include "session.h" /* XXX for child_set_env(); refactor? */
65
68
66
/* import */
69
/* import */
67
extern ServerOptions options;
70
extern ServerOptions options;
Lines 245-250 pubkey_auth_info(Authctxt *authctxt, const Key *key, const char *fmt, ...) Link Here
245
	free(extra);
248
	free(extra);
246
}
249
}
247
250
251
/*
252
 * Splits 's' into an argument vector. Handles quoted string and basic
253
 * escape characters (\\, \", \'). Caller must free the argument vector
254
 * and its members.
255
 */
256
static int
257
split_argv(const char *s, int *argcp, char ***argvp)
258
{
259
	int r = SSH_ERR_INTERNAL_ERROR;
260
	int argc = 0, quote, i, j;
261
	char *arg, **argv = xcalloc(1, sizeof(*argv));
262
263
	*argvp = NULL;
264
	*argcp = 0;
265
266
	for (i = 0; s[i] != '\0'; i++) {
267
		/* Skip leading whitespace */
268
		if (s[i] == ' ' || s[i] == '\t')
269
			continue;
270
271
		/* Start of a token */
272
		quote = 0;
273
		if (s[i] == '\\' &&
274
		    (s[i + 1] == '\'' || s[i + 1] == '\"' || s[i + 1] == '\\'))
275
			i++;
276
		else if (s[i] == '\'' || s[i] == '"')
277
			quote = s[i++];
278
279
		argv = xrealloc(argv, (argc + 2), sizeof(*argv));
280
		arg = argv[argc++] = xcalloc(1, strlen(s + i) + 1);
281
		argv[argc] = NULL;
282
283
		/* Copy the token in, removing escapes */
284
		for (j = 0; s[i] != '\0'; i++) {
285
			if (s[i] == '\\') {
286
				if (s[i + 1] == '\'' ||
287
				    s[i + 1] == '\"' ||
288
				    s[i + 1] == '\\') {
289
					i++; /* Skip '\' */
290
					arg[j++] = s[i];
291
				} else {
292
					/* Unrecognised escape */
293
					arg[j++] = s[i];
294
				}
295
			} else if (quote == 0 && (s[i] == ' ' || s[i] == '\t'))
296
				break; /* done */
297
			else if (quote != 0 && s[i] == quote)
298
				break; /* done */
299
			else
300
				arg[j++] = s[i];
301
		}
302
		if (s[i] == '\0') {
303
			if (quote != 0) {
304
				/* Ran out of string looking for close quote */
305
				r = SSH_ERR_INVALID_FORMAT;
306
				goto out;
307
			}
308
			break;
309
		}
310
	}
311
	/* Success */
312
	*argcp = argc;
313
	*argvp = argv;
314
	argc = 0;
315
	argv = NULL;
316
	r = 0;
317
 out:
318
	if (argc != 0 && argv != NULL) {
319
		for (i = 0; i < argc; i++)
320
			free(argv[i]);
321
		free(argv);
322
	}
323
	return r;
324
}
325
326
/*
327
 * Runs command in a subprocess. Returns pid on success and a FILE* to the
328
 * subprocess' stdout or 0 on failure.
329
 * NB. "command" is only used for logging.
330
 */
331
static pid_t
332
subprocess(const char *tag, struct passwd *pw, const char *command,
333
    int ac, char **av, FILE **child)
334
{
335
	FILE *f;
336
	struct stat st;
337
	int devnull, p[2], i;
338
	pid_t pid;
339
	char *cp, errmsg[512];
340
	u_int envsize;
341
	char **child_env;
342
343
	*child = NULL;
344
345
	debug3("%s: %s command \"%s\" running as %s", __func__,
346
	    tag, command, pw->pw_name);
347
348
	/* Verify the path exists and is safe-ish to execute */
349
	if (*av[0] != '/') {
350
		error("%s path is not absolute", tag);
351
		return 0;
352
	}
353
	temporarily_use_uid(pw);
354
	if (stat(av[0], &st) < 0) {
355
		error("Could not stat %s \"%s\": %s", tag,
356
		    av[0], strerror(errno));
357
		restore_uid();
358
		return 0;
359
	}
360
	if (auth_secure_path(av[0], &st, NULL, 0,
361
	    errmsg, sizeof(errmsg)) != 0) {
362
		error("Unsafe %s \"%s\": %s", tag, av[0], errmsg);
363
		restore_uid();
364
		return 0;
365
	}
366
367
	/*
368
	 * Run the command; stderr is left in place, stdout is the
369
	 * authorized_keys output.
370
	 */
371
	if (pipe(p) != 0) {
372
		error("%s: pipe: %s", tag, strerror(errno));
373
		restore_uid();
374
		return 0;
375
	}
376
377
	/*
378
	 * Don't want to call this in the child, where it can fatal() and
379
	 * run cleanup_exit() code.
380
	 */
381
	restore_uid();
382
383
	switch ((pid = fork())) {
384
	case -1: /* error */
385
		error("%s: fork: %s", tag, strerror(errno));
386
		close(p[0]);
387
		close(p[1]);
388
		return 0;
389
	case 0: /* child */
390
		/* Prepare a minimal environment for the child. */
391
		envsize = 5;
392
		child_env = xcalloc(sizeof(*child_env), envsize);
393
		child_set_env(&child_env, &envsize, "PATH", _PATH_STDPATH);
394
		child_set_env(&child_env, &envsize, "USER", pw->pw_name);
395
		child_set_env(&child_env, &envsize, "LOGNAME", pw->pw_name);
396
		child_set_env(&child_env, &envsize, "HOME", pw->pw_dir);
397
		if ((cp = getenv("LANG")) != NULL)
398
			child_set_env(&child_env, &envsize, "LANG", cp);
399
400
		for (i = 0; i < NSIG; i++)
401
			signal(i, SIG_DFL);
402
403
		if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) {
404
			error("%s: open %s: %s", tag, _PATH_DEVNULL,
405
			    strerror(errno));
406
			_exit(1);
407
		}
408
		/* Keep stderr around a while longer to catch errors */
409
		if (dup2(devnull, STDIN_FILENO) == -1 ||
410
		    dup2(p[1], STDOUT_FILENO) == -1) {
411
			error("%s: dup2: %s", tag, strerror(errno));
412
			_exit(1);
413
		}
414
		closefrom(STDERR_FILENO + 1);
415
416
		/* Don't use permanently_set_uid() here to avoid fatal() */
417
		if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) {
418
			error("%s: setresgid %u: %s", tag, (u_int)pw->pw_gid,
419
			    strerror(errno));
420
			_exit(1);
421
		}
422
		if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0) {
423
			error("%s: setresuid %u: %s", tag, (u_int)pw->pw_uid,
424
			    strerror(errno));
425
			_exit(1);
426
		}
427
		/* stdin is pointed to /dev/null at this point */
428
		if (dup2(STDIN_FILENO, STDERR_FILENO) == -1) {
429
			error("%s: dup2: %s", tag, strerror(errno));
430
			_exit(1);
431
		}
432
433
		execve(av[0], av, child_env);
434
		error("%s exec \"%s\": %s", tag, command, strerror(errno));
435
		_exit(127);
436
	default: /* parent */
437
		break;
438
	}
439
440
	close(p[1]);
441
	if ((f = fdopen(p[0], "r")) == NULL) {
442
		error("%s: fdopen: %s", tag, strerror(errno));
443
		close(p[0]);
444
		/* Don't leave zombie child */
445
		kill(pid, SIGTERM);
446
		while (waitpid(pid, NULL, 0) == -1 && errno == EINTR)
447
			;
448
		return 0;
449
	}
450
	/* Success */
451
	debug3("%s: %s pid %ld", __func__, tag, (long)pid);
452
	*child = f;
453
	return pid;
454
}
455
456
/* Returns 0 if pid exited cleanly, non-zero otherwise */
457
static int
458
exited_cleanly(pid_t pid, const char *tag, const char *cmd)
459
{
460
	int status;
461
462
	while (waitpid(pid, &status, 0) == -1) {
463
		if (errno != EINTR) {
464
			error("%s: waitpid: %s", tag, strerror(errno));
465
			return -1;
466
		}
467
	}
468
	if (WIFSIGNALED(status)) {
469
		error("%s %s exited on signal %d", tag, cmd, WTERMSIG(status));
470
		return -1;
471
	} else if (WEXITSTATUS(status) != 0) {
472
		error("%s %s failed, status %d", tag, cmd, WEXITSTATUS(status));
473
		return -1;
474
	}
475
	return 0;
476
}
477
248
static int
478
static int
249
match_principals_option(const char *principal_list, struct sshkey_cert *cert)
479
match_principals_option(const char *principal_list, struct sshkey_cert *cert)
250
{
480
{
Lines 266-284 match_principals_option(const char *principal_list, struct sshkey_cert *cert) Link Here
266
}
496
}
267
497
268
static int
498
static int
269
match_principals_file(char *file, struct passwd *pw, struct sshkey_cert *cert)
499
process_principals(FILE *f, char *file, struct passwd *pw,
500
    struct sshkey_cert *cert)
270
{
501
{
271
	FILE *f;
272
	char line[SSH_MAX_PUBKEY_BYTES], *cp, *ep, *line_opts;
502
	char line[SSH_MAX_PUBKEY_BYTES], *cp, *ep, *line_opts;
273
	u_long linenum = 0;
503
	u_long linenum = 0;
274
	u_int i;
504
	u_int i;
275
505
276
	temporarily_use_uid(pw);
277
	debug("trying authorized principals file %s", file);
278
	if ((f = auth_openprincipals(file, pw, options.strict_modes)) == NULL) {
279
		restore_uid();
280
		return 0;
281
	}
282
	while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) {
506
	while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) {
283
		/* Skip leading whitespace. */
507
		/* Skip leading whitespace. */
284
		for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
508
		for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
Lines 306-329 match_principals_file(char *file, struct passwd *pw, struct sshkey_cert *cert) Link Here
306
		}
530
		}
307
		for (i = 0; i < cert->nprincipals; i++) {
531
		for (i = 0; i < cert->nprincipals; i++) {
308
			if (strcmp(cp, cert->principals[i]) == 0) {
532
			if (strcmp(cp, cert->principals[i]) == 0) {
309
				debug3("matched principal \"%.100s\" "
533
				debug3("%s:%lu: matched principal \"%.100s\"",
310
				    "from file \"%s\" on line %lu",
534
				    file == NULL ? "(command)" : file,
311
				    cert->principals[i], file, linenum);
535
				    linenum, cert->principals[i]);
312
				if (auth_parse_options(pw, line_opts,
536
				if (auth_parse_options(pw, line_opts,
313
				    file, linenum) != 1)
537
				    file, linenum) != 1)
314
					continue;
538
					continue;
315
				fclose(f);
316
				restore_uid();
317
				return 1;
539
				return 1;
318
			}
540
			}
319
		}
541
		}
320
	}
542
	}
543
	return 0;
544
}
545
546
static int
547
match_principals_file(char *file, struct passwd *pw, struct sshkey_cert *cert)
548
{
549
	FILE *f;
550
	int success;
551
552
	temporarily_use_uid(pw);
553
	debug("trying authorized principals file %s", file);
554
	if ((f = auth_openprincipals(file, pw, options.strict_modes)) == NULL) {
555
		restore_uid();
556
		return 0;
557
	}
558
	success = process_principals(f, file, pw, cert);
321
	fclose(f);
559
	fclose(f);
322
	restore_uid();
560
	restore_uid();
323
	return 0;
561
	return success;
324
}
562
}
325
563
326
/*
564
/*
565
 * Checks whether principal is allowed in output of command.
566
 * returns 1 if the principal is allowed or 0 otherwise.
567
 */
568
static int
569
match_principals_command(struct passwd *user_pw, struct sshkey *key)
570
{
571
	FILE *f = NULL;
572
	int ok, found_principal = 0;
573
	struct passwd *pw;
574
	int i, ac = 0, uid_swapped = 0;
575
	pid_t pid;
576
	char *username = NULL, *command = NULL, **av = NULL;
577
	void (*osigchld)(int);
578
579
	if (options.authorized_principals_command == NULL)
580
		return 0;
581
	if (options.authorized_principals_command_user == NULL) {
582
		error("No user for AuthorizedPrincipalsCommand specified, "
583
		    "skipping");
584
		return 0;
585
	}
586
587
	/*
588
	 * NB. all returns later this function should go via "out" to
589
	 * ensure the original SIGCHLD handler is restored properly.
590
	 */
591
	osigchld = signal(SIGCHLD, SIG_DFL);
592
593
	/* Prepare and verify the user for the command */
594
	username = percent_expand(options.authorized_principals_command_user,
595
	    "u", user_pw->pw_name, (char *)NULL);
596
	pw = getpwnam(username);
597
	if (pw == NULL) {
598
		error("AuthorizedPrincipalsCommandUser \"%s\" not found: %s",
599
		    username, strerror(errno));
600
		goto out;
601
	}
602
603
	command = percent_expand(options.authorized_principals_command,
604
	    "u", user_pw->pw_name, "h", user_pw->pw_dir, (char *)NULL);
605
606
	/* Turn the command into an argument vector */
607
	if (split_argv(command, &ac, &av) != 0) {
608
		error("AuthorizedPrincipalsCommand \"%s\" contains "
609
		    "invalid quotes", command);
610
		goto out;
611
	}
612
	if (ac == 0) {
613
		error("AuthorizedPrincipalsCommand \"%s\" yielded no arguments",
614
		    command);
615
		goto out;
616
	}
617
618
	if ((pid = subprocess("AuthorizedPrincipalsCommand", pw, command,
619
	    ac, av, &f)) == 0)
620
		goto out;
621
622
	uid_swapped = 1;
623
	temporarily_use_uid(pw);
624
625
	ok = process_principals(f, NULL, pw, key->cert);
626
627
	if (exited_cleanly(pid, "AuthorizedPrincipalsCommand", command))
628
		goto out;
629
630
	/* Read completed successfully */
631
	found_principal = ok;
632
 out:
633
	if (f != NULL)
634
		fclose(f);
635
	signal(SIGCHLD, osigchld);
636
	for (i = 0; i < ac; i++)
637
		free(av[i]);
638
	free(av);
639
	if (uid_swapped)
640
		restore_uid();
641
	free(command);
642
	free(username);
643
	return found_principal;
644
}
645
/*
327
 * Checks whether key is allowed in authorized_keys-format file,
646
 * Checks whether key is allowed in authorized_keys-format file,
328
 * returns 1 if the key is allowed or 0 otherwise.
647
 * returns 1 if the key is allowed or 0 otherwise.
329
 */
648
 */
Lines 445-451 user_cert_trusted_ca(struct passwd *pw, Key *key) Link Here
445
{
764
{
446
	char *ca_fp, *principals_file = NULL;
765
	char *ca_fp, *principals_file = NULL;
447
	const char *reason;
766
	const char *reason;
448
	int ret = 0;
767
	int ret = 0, found_principal = 0;
449
768
450
	if (!key_is_cert(key) || options.trusted_user_ca_keys == NULL)
769
	if (!key_is_cert(key) || options.trusted_user_ca_keys == NULL)
451
		return 0;
770
		return 0;
Lines 467-480 user_cert_trusted_ca(struct passwd *pw, Key *key) Link Here
467
	 * against the username.
786
	 * against the username.
468
	 */
787
	 */
469
	if ((principals_file = authorized_principals_file(pw)) != NULL) {
788
	if ((principals_file = authorized_principals_file(pw)) != NULL) {
470
		if (!match_principals_file(principals_file, pw, key->cert)) {
789
		if (match_principals_file(principals_file, pw, key->cert))
471
			reason = "Certificate does not contain an "
790
			found_principal = 1;
472
			    "authorized principal";
791
	}
792
	/* Try querying command if specified */
793
	if (!found_principal && match_principals_command(pw, key))
794
		found_principal = 1;
795
	/* If principals file or command specify, then require a match here */
796
	if (!found_principal && (principals_file != NULL ||
797
	    options.authorized_principals_command != NULL)) {
798
		reason = "Certificate does not contain an authorized principal";
473
 fail_reason:
799
 fail_reason:
474
			error("%s", reason);
800
		error("%s", reason);
475
			auth_debug_add("%s", reason);
801
		auth_debug_add("%s", reason);
476
			goto out;
802
		goto out;
477
		}
478
	}
803
	}
479
	if (key_cert_check_authority(key, 0, 1,
804
	if (key_cert_check_authority(key, 0, 1,
480
	    principals_file == NULL ? pw->pw_name : NULL, &reason) != 0)
805
	    principals_file == NULL ? pw->pw_name : NULL, &reason) != 0)
Lines 523-666 user_key_allowed2(struct passwd *pw, Key *key, char *file) Link Here
523
static int
848
static int
524
user_key_command_allowed2(struct passwd *user_pw, Key *key)
849
user_key_command_allowed2(struct passwd *user_pw, Key *key)
525
{
850
{
526
	FILE *f;
851
	FILE *f = NULL;
527
	int ok, found_key = 0;
852
	int r, ok, found_key = 0;
528
	struct passwd *pw;
853
	struct passwd *pw;
529
	struct stat st;
854
	int i, uid_swapped = 0, ac = 0;
530
	int status, devnull, p[2], i;
531
	pid_t pid;
855
	pid_t pid;
532
	char *username, errmsg[512];
856
	char *username = NULL, *key_fp = NULL, *keytext = NULL;
857
	char *command = NULL, **av = NULL;
858
	void (*osigchld)(int);
533
859
534
	if (options.authorized_keys_command == NULL ||
860
	if (options.authorized_keys_command == NULL)
535
	    options.authorized_keys_command[0] != '/')
536
		return 0;
861
		return 0;
537
538
	if (options.authorized_keys_command_user == NULL) {
862
	if (options.authorized_keys_command_user == NULL) {
539
		error("No user for AuthorizedKeysCommand specified, skipping");
863
		error("No user for AuthorizedKeysCommand specified, skipping");
540
		return 0;
864
		return 0;
541
	}
865
	}
542
866
867
	/*
868
	 * NB. all returns later this function should go via "out" to
869
	 * ensure the original SIGCHLD handler is restored properly.
870
	 */
871
	osigchld = signal(SIGCHLD, SIG_DFL);
872
873
	/* Prepare and verify the user for the command */
543
	username = percent_expand(options.authorized_keys_command_user,
874
	username = percent_expand(options.authorized_keys_command_user,
544
	    "u", user_pw->pw_name, (char *)NULL);
875
	    "u", user_pw->pw_name, (char *)NULL);
545
	pw = getpwnam(username);
876
	pw = getpwnam(username);
546
	if (pw == NULL) {
877
	if (pw == NULL) {
547
		error("AuthorizedKeysCommandUser \"%s\" not found: %s",
878
		error("AuthorizedKeysCommandUser \"%s\" not found: %s",
548
		    username, strerror(errno));
879
		    username, strerror(errno));
549
		free(username);
880
		goto out;
550
		return 0;
551
	}
881
	}
552
	free(username);
553
554
	temporarily_use_uid(pw);
555
882
556
	if (stat(options.authorized_keys_command, &st) < 0) {
883
	/* Prepare AuthorizedKeysCommand */
557
		error("Could not stat AuthorizedKeysCommand \"%s\": %s",
884
	if ((key_fp = sshkey_fingerprint(key, options.fingerprint_hash,
558
		    options.authorized_keys_command, strerror(errno));
885
	    SSH_FP_DEFAULT)) == NULL) {
886
		error("%s: sshkey_fingerprint failed", __func__);
559
		goto out;
887
		goto out;
560
	}
888
	}
561
	if (auth_secure_path(options.authorized_keys_command, &st, NULL, 0,
889
	if ((r = sshkey_to_base64(key, &keytext)) != 0) {
562
	    errmsg, sizeof(errmsg)) != 0) {
890
		error("%s: sshkey_to_base64 failed: %s", __func__, ssh_err(r));
563
		error("Unsafe AuthorizedKeysCommand: %s", errmsg);
564
		goto out;
891
		goto out;
565
	}
892
	}
893
	command = percent_expand(options.authorized_keys_command,
894
	    "u", user_pw->pw_name, "h", user_pw->pw_dir,
895
	    "t", sshkey_ssh_name(key), "f", key_fp, "k", keytext, (char *)NULL);
566
896
567
	if (pipe(p) != 0) {
897
	/* Turn the command into an argument vector */
568
		error("%s: pipe: %s", __func__, strerror(errno));
898
	if (split_argv(command, &ac, &av) != 0) {
899
		error("AuthorizedKeysCommand \"%s\" contains invalid quotes",
900
		    command);
901
		goto out;
902
	}
903
	if (ac == 0) {
904
		error("AuthorizedKeysCommand \"%s\" yielded no arguments",
905
		    command);
569
		goto out;
906
		goto out;
570
	}
907
	}
571
572
	debug3("Running AuthorizedKeysCommand: \"%s %s\" as \"%s\"",
573
	    options.authorized_keys_command, user_pw->pw_name, pw->pw_name);
574
908
575
	/*
909
	/*
576
	 * Don't want to call this in the child, where it can fatal() and
910
	 * If AuthorizedKeysCommand was run without arguments
577
	 * run cleanup_exit() code.
911
	 * then fall back to the old behaviour of passing the
912
	 * target username as a single argument.
578
	 */
913
	 */
579
	restore_uid();
914
	if (ac == 1) {
580
915
		av = xrealloc(av, ac + 2, sizeof(*av));
581
	switch ((pid = fork())) {
916
		av[1] = xstrdup(user_pw->pw_name);
582
	case -1: /* error */
917
		av[2] = NULL;
583
		error("%s: fork: %s", __func__, strerror(errno));
918
		/* Fix up command too, since it is used in log messages */
584
		close(p[0]);
919
		free(command);
585
		close(p[1]);
920
		xasprintf(&command, "%s %s", av[0], av[1]);
586
		return 0;
587
	case 0: /* child */
588
		for (i = 0; i < NSIG; i++)
589
			signal(i, SIG_DFL);
590
591
		if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) {
592
			error("%s: open %s: %s", __func__, _PATH_DEVNULL,
593
			    strerror(errno));
594
			_exit(1);
595
		}
596
		/* Keep stderr around a while longer to catch errors */
597
		if (dup2(devnull, STDIN_FILENO) == -1 ||
598
		    dup2(p[1], STDOUT_FILENO) == -1) {
599
			error("%s: dup2: %s", __func__, strerror(errno));
600
			_exit(1);
601
		}
602
		closefrom(STDERR_FILENO + 1);
603
604
		/* Don't use permanently_set_uid() here to avoid fatal() */
605
		if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) {
606
			error("setresgid %u: %s", (u_int)pw->pw_gid,
607
			    strerror(errno));
608
			_exit(1);
609
		}
610
		if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0) {
611
			error("setresuid %u: %s", (u_int)pw->pw_uid,
612
			    strerror(errno));
613
			_exit(1);
614
		}
615
		/* stdin is pointed to /dev/null at this point */
616
		if (dup2(STDIN_FILENO, STDERR_FILENO) == -1) {
617
			error("%s: dup2: %s", __func__, strerror(errno));
618
			_exit(1);
619
		}
620
621
		execl(options.authorized_keys_command,
622
		    options.authorized_keys_command, user_pw->pw_name, NULL);
623
624
		error("AuthorizedKeysCommand %s exec failed: %s",
625
		    options.authorized_keys_command, strerror(errno));
626
		_exit(127);
627
	default: /* parent */
628
		break;
629
	}
921
	}
630
922
631
	temporarily_use_uid(pw);
923
	if ((pid = subprocess("AuthorizedKeysCommand", pw, command,
632
924
	    ac, av, &f)) == 0)
633
	close(p[1]);
634
	if ((f = fdopen(p[0], "r")) == NULL) {
635
		error("%s: fdopen: %s", __func__, strerror(errno));
636
		close(p[0]);
637
		/* Don't leave zombie child */
638
		kill(pid, SIGTERM);
639
		while (waitpid(pid, NULL, 0) == -1 && errno == EINTR)
640
			;
641
		goto out;
925
		goto out;
642
	}
926
927
	uid_swapped = 1;
928
	temporarily_use_uid(pw);
929
643
	ok = check_authkeys_file(f, options.authorized_keys_command, key, pw);
930
	ok = check_authkeys_file(f, options.authorized_keys_command, key, pw);
644
	fclose(f);
645
931
646
	while (waitpid(pid, &status, 0) == -1) {
932
	if (exited_cleanly(pid, "AuthorizedKeysCommand", command))
647
		if (errno != EINTR) {
648
			error("%s: waitpid: %s", __func__, strerror(errno));
649
			goto out;
650
		}
651
	}
652
	if (WIFSIGNALED(status)) {
653
		error("AuthorizedKeysCommand %s exited on signal %d",
654
		    options.authorized_keys_command, WTERMSIG(status));
655
		goto out;
933
		goto out;
656
	} else if (WEXITSTATUS(status) != 0) {
934
657
		error("AuthorizedKeysCommand %s returned status %d",
935
	/* Read completed successfully */
658
		    options.authorized_keys_command, WEXITSTATUS(status));
659
		goto out;
660
	}
661
	found_key = ok;
936
	found_key = ok;
662
 out:
937
 out:
663
	restore_uid();
938
	if (f != NULL)
939
		fclose(f);
940
	signal(SIGCHLD, osigchld);
941
	for (i = 0; i < ac; i++)
942
		free(av[i]);
943
	free(av);
944
	if (uid_swapped)
945
		restore_uid();
946
	free(command);
947
	free(username);
948
	free(key_fp);
949
	free(keytext);
664
	return found_key;
950
	return found_key;
665
}
951
}
666
952
(-)a/servconf.c (+35 lines)
Lines 149-154 initialize_server_options(ServerOptions *options) Link Here
149
	options->revoked_keys_file = NULL;
149
	options->revoked_keys_file = NULL;
150
	options->trusted_user_ca_keys = NULL;
150
	options->trusted_user_ca_keys = NULL;
151
	options->authorized_principals_file = NULL;
151
	options->authorized_principals_file = NULL;
152
	options->authorized_principals_command = NULL;
153
	options->authorized_principals_command_user = NULL;
152
	options->ip_qos_interactive = -1;
154
	options->ip_qos_interactive = -1;
153
	options->ip_qos_bulk = -1;
155
	options->ip_qos_bulk = -1;
154
	options->version_addendum = NULL;
156
	options->version_addendum = NULL;
Lines 366-371 typedef enum { Link Here
366
	sUsePrivilegeSeparation, sAllowAgentForwarding,
368
	sUsePrivilegeSeparation, sAllowAgentForwarding,
367
	sHostCertificate,
369
	sHostCertificate,
368
	sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile,
370
	sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile,
371
	sAuthorizedPrincipalsCommand, sAuthorizedPrincipalsCommandUser,
369
	sKexAlgorithms, sIPQoS, sVersionAddendum,
372
	sKexAlgorithms, sIPQoS, sVersionAddendum,
370
	sAuthorizedKeysCommand, sAuthorizedKeysCommandUser,
373
	sAuthorizedKeysCommand, sAuthorizedKeysCommandUser,
371
	sAuthenticationMethods, sHostKeyAgent, sPermitUserRC,
374
	sAuthenticationMethods, sHostKeyAgent, sPermitUserRC,
Lines 486-491 static struct { Link Here
486
	{ "ipqos", sIPQoS, SSHCFG_ALL },
489
	{ "ipqos", sIPQoS, SSHCFG_ALL },
487
	{ "authorizedkeyscommand", sAuthorizedKeysCommand, SSHCFG_ALL },
490
	{ "authorizedkeyscommand", sAuthorizedKeysCommand, SSHCFG_ALL },
488
	{ "authorizedkeyscommanduser", sAuthorizedKeysCommandUser, SSHCFG_ALL },
491
	{ "authorizedkeyscommanduser", sAuthorizedKeysCommandUser, SSHCFG_ALL },
492
	{ "authorizedprincipalscommand", sAuthorizedPrincipalsCommand, SSHCFG_ALL },
493
	{ "authorizedprincipalscommanduser", sAuthorizedPrincipalsCommandUser, SSHCFG_ALL },
489
	{ "versionaddendum", sVersionAddendum, SSHCFG_GLOBAL },
494
	{ "versionaddendum", sVersionAddendum, SSHCFG_GLOBAL },
490
	{ "authenticationmethods", sAuthenticationMethods, SSHCFG_ALL },
495
	{ "authenticationmethods", sAuthenticationMethods, SSHCFG_ALL },
491
	{ "streamlocalbindmask", sStreamLocalBindMask, SSHCFG_ALL },
496
	{ "streamlocalbindmask", sStreamLocalBindMask, SSHCFG_ALL },
Lines 1649-1654 process_server_config_line(ServerOptions *options, char *line, Link Here
1649
			*charptr = xstrdup(arg);
1654
			*charptr = xstrdup(arg);
1650
		break;
1655
		break;
1651
1656
1657
	case sAuthorizedPrincipalsCommand:
1658
		if (cp == NULL)
1659
			fatal("%.200s line %d: Missing argument.", filename,
1660
			    linenum);
1661
		len = strspn(cp, WHITESPACE);
1662
		if (*activep &&
1663
		    options->authorized_principals_command == NULL) {
1664
			if (cp[len] != '/' && strcasecmp(cp + len, "none") != 0)
1665
				fatal("%.200s line %d: "
1666
				    "AuthorizedPrincipalsCommand must be "
1667
				    "an absolute path", filename, linenum);
1668
			options->authorized_principals_command =
1669
			    xstrdup(cp + len);
1670
		}
1671
		return 0;
1672
1673
	case sAuthorizedPrincipalsCommandUser:
1674
		charptr = &options->authorized_principals_command_user;
1675
1676
		arg = strdelim(&cp);
1677
		if (!arg || *arg == '\0')
1678
			fatal("%s line %d: missing "
1679
			    "AuthorizedPrincipalsCommandUser argument.",
1680
			    filename, linenum);
1681
		if (*activep && *charptr == NULL)
1682
			*charptr = xstrdup(arg);
1683
		break;
1684
1652
	case sAuthenticationMethods:
1685
	case sAuthenticationMethods:
1653
		if (*activep && options->num_auth_methods == 0) {
1686
		if (*activep && options->num_auth_methods == 0) {
1654
			while ((arg = strdelim(&cp)) && *arg != '\0') {
1687
			while ((arg = strdelim(&cp)) && *arg != '\0') {
Lines 2113-2118 dump_config(ServerOptions *o) Link Here
2113
	dump_cfg_string(sVersionAddendum, o->version_addendum);
2146
	dump_cfg_string(sVersionAddendum, o->version_addendum);
2114
	dump_cfg_string(sAuthorizedKeysCommand, o->authorized_keys_command);
2147
	dump_cfg_string(sAuthorizedKeysCommand, o->authorized_keys_command);
2115
	dump_cfg_string(sAuthorizedKeysCommandUser, o->authorized_keys_command_user);
2148
	dump_cfg_string(sAuthorizedKeysCommandUser, o->authorized_keys_command_user);
2149
	dump_cfg_string(sAuthorizedPrincipalsCommand, o->authorized_principals_command);
2150
	dump_cfg_string(sAuthorizedPrincipalsCommandUser, o->authorized_principals_command_user);
2116
	dump_cfg_string(sHostKeyAgent, o->host_key_agent);
2151
	dump_cfg_string(sHostKeyAgent, o->host_key_agent);
2117
	dump_cfg_string(sKexAlgorithms,
2152
	dump_cfg_string(sKexAlgorithms,
2118
	    o->kex_algorithms ? o->kex_algorithms : KEX_SERVER_KEX);
2153
	    o->kex_algorithms ? o->kex_algorithms : KEX_SERVER_KEX);
(-)a/servconf.h (-2 / +6 lines)
Lines 174-182 typedef struct { Link Here
174
	char   *chroot_directory;
174
	char   *chroot_directory;
175
	char   *revoked_keys_file;
175
	char   *revoked_keys_file;
176
	char   *trusted_user_ca_keys;
176
	char   *trusted_user_ca_keys;
177
	char   *authorized_principals_file;
178
	char   *authorized_keys_command;
177
	char   *authorized_keys_command;
179
	char   *authorized_keys_command_user;
178
	char   *authorized_keys_command_user;
179
	char   *authorized_principals_file;
180
	char   *authorized_principals_command;
181
	char   *authorized_principals_command_user;
180
182
181
	int64_t rekey_limit;
183
	int64_t rekey_limit;
182
	int	rekey_interval;
184
	int	rekey_interval;
Lines 212-220 struct connection_info { Link Here
212
		M_CP_STROPT(banner); \
214
		M_CP_STROPT(banner); \
213
		M_CP_STROPT(trusted_user_ca_keys); \
215
		M_CP_STROPT(trusted_user_ca_keys); \
214
		M_CP_STROPT(revoked_keys_file); \
216
		M_CP_STROPT(revoked_keys_file); \
215
		M_CP_STROPT(authorized_principals_file); \
216
		M_CP_STROPT(authorized_keys_command); \
217
		M_CP_STROPT(authorized_keys_command); \
217
		M_CP_STROPT(authorized_keys_command_user); \
218
		M_CP_STROPT(authorized_keys_command_user); \
219
		M_CP_STROPT(authorized_principals_file); \
220
		M_CP_STROPT(authorized_principals_command); \
221
		M_CP_STROPT(authorized_principals_command_user); \
218
		M_CP_STROPT(hostbased_key_types); \
222
		M_CP_STROPT(hostbased_key_types); \
219
		M_CP_STROPT(pubkey_key_types); \
223
		M_CP_STROPT(pubkey_key_types); \
220
		M_CP_STRARRAYOPT(authorized_keys_files, num_authkeys_files); \
224
		M_CP_STRARRAYOPT(authorized_keys_files, num_authkeys_files); \
(-)a/sshd.c (+5 lines)
Lines 1596-1601 main(int ac, char **av) Link Here
1596
	    strcasecmp(options.authorized_keys_command, "none") != 0))
1596
	    strcasecmp(options.authorized_keys_command, "none") != 0))
1597
		fatal("AuthorizedKeysCommand set without "
1597
		fatal("AuthorizedKeysCommand set without "
1598
		    "AuthorizedKeysCommandUser");
1598
		    "AuthorizedKeysCommandUser");
1599
	if (options.authorized_principals_command_user == NULL &&
1600
	    (options.authorized_principals_command != NULL &&
1601
	    strcasecmp(options.authorized_principals_command, "none") != 0))
1602
		fatal("AuthorizedPrincipalsCommand set without "
1603
		    "AuthorizedPrincipalsCommandUser");
1599
1604
1600
	/*
1605
	/*
1601
	 * Check whether there is any path through configured auth methods.
1606
	 * Check whether there is any path through configured auth methods.
(-)a/sshd_config.5 (-3 / +51 lines)
Lines 230-238 The default is not to require multiple authentication; successful completion Link Here
230
of a single authentication method is sufficient.
230
of a single authentication method is sufficient.
231
.It Cm AuthorizedKeysCommand
231
.It Cm AuthorizedKeysCommand
232
Specifies a program to be used to look up the user's public keys.
232
Specifies a program to be used to look up the user's public keys.
233
The program must be owned by root and not writable by group or others.
233
The program must be owned by root, not writable by group or others and
234
It will be invoked with a single argument of the username
234
specified by an absolute path.
235
being authenticated, and should produce on standard output zero or
235
.Pp
236
Arguments to
237
.Cm AuthorizedKeysCommand
238
may be provided using the following tokens, which will be expanded
239
at runtime: %% is replaced by a literal '%', %u is replaced by the
240
username being authenticated, %h is replaced by the home directory
241
of the user being authenticated, %t is replaced with the key type
242
offered for authentication, %f is replaced with the fingerprint of
243
the key, and %k is replaced with the key being offered for authentication.
244
If no arguments are specified then the username of the target user
245
will be supplied.
246
.Pp
247
The program should produce on standard output zero or
236
more lines of authorized_keys output (see AUTHORIZED_KEYS in
248
more lines of authorized_keys output (see AUTHORIZED_KEYS in
237
.Xr sshd 8 ) .
249
.Xr sshd 8 ) .
238
If a key supplied by AuthorizedKeysCommand does not successfully authenticate
250
If a key supplied by AuthorizedKeysCommand does not successfully authenticate
Lines 271-276 directory. Link Here
271
Multiple files may be listed, separated by whitespace.
283
Multiple files may be listed, separated by whitespace.
272
The default is
284
The default is
273
.Dq .ssh/authorized_keys .ssh/authorized_keys2 .
285
.Dq .ssh/authorized_keys .ssh/authorized_keys2 .
286
.It Cm AuthorizedPrincipalsCommand
287
Specifies a program to be used to generate the list of allowed
288
certificate principals as per
289
.Cm AuthorizedPrincipalsFile .
290
The program must be owned by root, not writable by group or others and
291
specified by an absolute path.
292
.Pp
293
Arguments to
294
.Cm AuthorizedPrincipalsCommand
295
may be provided using the following tokens, which will be expanded
296
at runtime: %% is replaced by a literal '%', %u is replaced by the
297
username being authenticated and %h is replaced by the home directory
298
of the user being authenticated.
299
.Pp
300
The program should produce on standard output zero or
301
more lines of
302
.Cm AuthorizedPrincipalsFile
303
output.
304
If either
305
.Cm AuthorizedPrincipalsCommand
306
or
307
.Cm AuthorizedPrincipalsFile
308
is specified, then certificates offered by the client for authentication
309
must contain a principal that is listed.
310
By default, no AuthorizedPrincipalsCommand is run.
311
.It Cm AuthorizedPrincipalsCommandUser
312
Specifies the user under whose account the AuthorizedPrincipalsCommand is run.
313
It is recommended to use a dedicated user that has no other role on the host
314
than running authorized principals commands.
315
If
316
.Cm AuthorizedPrincipalsCommand
317
is specified but
318
.Cm AuthorizedPrincipalsCommandUser
319
is not, then
320
.Xr sshd 8
321
will refuse to start.
274
.It Cm AuthorizedPrincipalsFile
322
.It Cm AuthorizedPrincipalsFile
275
Specifies a file that lists principal names that are accepted for
323
Specifies a file that lists principal names that are accepted for
276
certificate authentication.
324
certificate authentication.
(-)a/sshkey.c (-75 / +99 lines)
Lines 737-742 to_blob_buf(const struct sshkey *key, struct sshbuf *b, int force_plain) Link Here
737
	if (key == NULL)
737
	if (key == NULL)
738
		return SSH_ERR_INVALID_ARGUMENT;
738
		return SSH_ERR_INVALID_ARGUMENT;
739
739
740
	if (sshkey_is_cert(key)) {
741
		if (key->cert == NULL)
742
			return SSH_ERR_EXPECTED_CERT;
743
		if (sshbuf_len(key->cert->certblob) == 0)
744
			return SSH_ERR_KEY_LACKS_CERTBLOB;
745
	}
740
	type = force_plain ? sshkey_type_plain(key->type) : key->type;
746
	type = force_plain ? sshkey_type_plain(key->type) : key->type;
741
	typename = sshkey_ssh_name_from_type_nid(type, key->ecdsa_nid);
747
	typename = sshkey_ssh_name_from_type_nid(type, key->ecdsa_nid);
742
748
Lines 1381-1478 sshkey_read(struct sshkey *ret, char **cpp) Link Here
1381
}
1387
}
1382
1388
1383
int
1389
int
1384
sshkey_write(const struct sshkey *key, FILE *f)
1390
sshkey_to_base64(const struct sshkey *key, char **b64p)
1385
{
1391
{
1386
	int ret = SSH_ERR_INTERNAL_ERROR;
1392
	int r = SSH_ERR_INTERNAL_ERROR;
1387
	struct sshbuf *b = NULL, *bb = NULL;
1393
	struct sshbuf *b = NULL;
1388
	char *uu = NULL;
1394
	char *uu = NULL;
1395
1396
	if (b64p != NULL)
1397
		*b64p = NULL;
1398
	if ((b = sshbuf_new()) == NULL)
1399
		return SSH_ERR_ALLOC_FAIL;
1400
	if ((r = sshkey_putb(key, b)) != 0)
1401
		goto out;
1402
	if ((uu = sshbuf_dtob64(b)) == NULL) {
1403
		r = SSH_ERR_ALLOC_FAIL;
1404
		goto out;
1405
	}
1406
	/* Success */
1407
	if (b64p != NULL) {
1408
		*b64p = uu;
1409
		uu = NULL;
1410
	}
1411
	r = 0;
1412
 out:
1413
	sshbuf_free(b);
1414
	free(uu);
1415
	return r;
1416
}
1417
1418
static int
1419
sshkey_format_rsa1(const struct sshkey *key, struct sshbuf *b)
1420
{
1421
	int r = SSH_ERR_INTERNAL_ERROR;
1389
#ifdef WITH_SSH1
1422
#ifdef WITH_SSH1
1390
	u_int bits = 0;
1423
	u_int bits = 0;
1391
	char *dec_e = NULL, *dec_n = NULL;
1424
	char *dec_e = NULL, *dec_n = NULL;
1392
#endif /* WITH_SSH1 */
1393
1425
1394
	if (sshkey_is_cert(key)) {
1426
	if (key->rsa == NULL || key->rsa->e == NULL ||
1395
		if (key->cert == NULL)
1427
	    key->rsa->n == NULL) {
1396
			return SSH_ERR_EXPECTED_CERT;
1428
		r = SSH_ERR_INVALID_ARGUMENT;
1397
		if (sshbuf_len(key->cert->certblob) == 0)
1429
		goto out;
1398
			return SSH_ERR_KEY_LACKS_CERTBLOB;
1399
	}
1430
	}
1400
	if ((b = sshbuf_new()) == NULL)
1431
	if ((dec_e = BN_bn2dec(key->rsa->e)) == NULL ||
1401
		return SSH_ERR_ALLOC_FAIL;
1432
	    (dec_n = BN_bn2dec(key->rsa->n)) == NULL) {
1402
	switch (key->type) {
1433
		r = SSH_ERR_ALLOC_FAIL;
1403
#ifdef WITH_SSH1
1404
	case KEY_RSA1:
1405
		if (key->rsa == NULL || key->rsa->e == NULL ||
1406
		    key->rsa->n == NULL) {
1407
			ret = SSH_ERR_INVALID_ARGUMENT;
1408
			goto out;
1409
		}
1410
		if ((dec_e = BN_bn2dec(key->rsa->e)) == NULL ||
1411
		    (dec_n = BN_bn2dec(key->rsa->n)) == NULL) {
1412
			ret = SSH_ERR_ALLOC_FAIL;
1413
			goto out;
1414
		}
1415
		/* size of modulus 'n' */
1416
		if ((bits = BN_num_bits(key->rsa->n)) <= 0) {
1417
			ret = SSH_ERR_INVALID_ARGUMENT;
1418
			goto out;
1419
		}
1420
		if ((ret = sshbuf_putf(b, "%u %s %s", bits, dec_e, dec_n)) != 0)
1421
			goto out;
1422
#endif /* WITH_SSH1 */
1423
		break;
1424
#ifdef WITH_OPENSSL
1425
	case KEY_DSA:
1426
	case KEY_DSA_CERT_V00:
1427
	case KEY_DSA_CERT:
1428
	case KEY_ECDSA:
1429
	case KEY_ECDSA_CERT:
1430
	case KEY_RSA:
1431
	case KEY_RSA_CERT_V00:
1432
	case KEY_RSA_CERT:
1433
#endif /* WITH_OPENSSL */
1434
	case KEY_ED25519:
1435
	case KEY_ED25519_CERT:
1436
		if ((bb = sshbuf_new()) == NULL) {
1437
			ret = SSH_ERR_ALLOC_FAIL;
1438
			goto out;
1439
		}
1440
		if ((ret = sshkey_putb(key, bb)) != 0)
1441
			goto out;
1442
		if ((uu = sshbuf_dtob64(bb)) == NULL) {
1443
			ret = SSH_ERR_ALLOC_FAIL;
1444
			goto out;
1445
		}
1446
		if ((ret = sshbuf_putf(b, "%s ", sshkey_ssh_name(key))) != 0)
1447
			goto out;
1448
		if ((ret = sshbuf_put(b, uu, strlen(uu))) != 0)
1449
			goto out;
1450
		break;
1451
	default:
1452
		ret = SSH_ERR_KEY_TYPE_UNKNOWN;
1453
		goto out;
1434
		goto out;
1454
	}
1435
	}
1455
	if (fwrite(sshbuf_ptr(b), sshbuf_len(b), 1, f) != 1) {
1436
	/* size of modulus 'n' */
1456
		if (feof(f))
1437
	if ((bits = BN_num_bits(key->rsa->n)) <= 0) {
1457
			errno = EPIPE;
1438
		r = SSH_ERR_INVALID_ARGUMENT;
1458
		ret = SSH_ERR_SYSTEM_ERROR;
1459
		goto out;
1439
		goto out;
1460
	}
1440
	}
1461
	ret = 0;
1441
	if ((r = sshbuf_putf(b, "%u %s %s", bits, dec_e, dec_n)) != 0)
1442
		goto out;
1443
1444
	/* Success */
1445
	r = 0;
1462
 out:
1446
 out:
1463
	if (b != NULL)
1464
		sshbuf_free(b);
1465
	if (bb != NULL)
1466
		sshbuf_free(bb);
1467
	if (uu != NULL)
1468
		free(uu);
1469
#ifdef WITH_SSH1
1470
	if (dec_e != NULL)
1447
	if (dec_e != NULL)
1471
		OPENSSL_free(dec_e);
1448
		OPENSSL_free(dec_e);
1472
	if (dec_n != NULL)
1449
	if (dec_n != NULL)
1473
		OPENSSL_free(dec_n);
1450
		OPENSSL_free(dec_n);
1474
#endif /* WITH_SSH1 */
1451
#endif /* WITH_SSH1 */
1475
	return ret;
1452
1453
	return r;
1454
}
1455
1456
static int
1457
sshkey_format_text(const struct sshkey *key, struct sshbuf *b)
1458
{
1459
	int r = SSH_ERR_INTERNAL_ERROR;
1460
	char *uu = NULL;
1461
1462
	if (key->type == KEY_RSA1) {
1463
		if ((r = sshkey_format_rsa1(key, b)) != 0)
1464
			goto out;
1465
	} else {
1466
		/* Unsupported key types handled in sshkey_to_base64() */
1467
		if ((r = sshkey_to_base64(key, &uu)) != 0)
1468
			goto out;
1469
		if ((r = sshbuf_putf(b, "%s %s",
1470
		    sshkey_ssh_name(key), uu)) != 0)
1471
			goto out;
1472
	}
1473
	r = 0;
1474
 out:
1475
	free(uu);
1476
	return r;
1477
}
1478
1479
int
1480
sshkey_write(const struct sshkey *key, FILE *f)
1481
{
1482
	struct sshbuf *b = NULL;
1483
	int r = SSH_ERR_INTERNAL_ERROR;
1484
1485
	if ((b = sshbuf_new()) == NULL)
1486
		return SSH_ERR_ALLOC_FAIL;
1487
	if ((r = sshkey_format_text(key, b)) != 0)
1488
		goto out;
1489
	if (fwrite(sshbuf_ptr(b), sshbuf_len(b), 1, f) != 1) {
1490
		if (feof(f))
1491
			errno = EPIPE;
1492
		r = SSH_ERR_SYSTEM_ERROR;
1493
		goto out;
1494
	}
1495
	/* Success */
1496
	r = 0;
1497
 out:
1498
	sshbuf_free(b);
1499
	return r;
1476
}
1500
}
1477
1501
1478
const char *
1502
const char *
(-)a/sshkey.h (+1 lines)
Lines 157-162 int sshkey_from_blob(const u_char *, size_t, struct sshkey **); Link Here
157
int	 sshkey_fromb(struct sshbuf *, struct sshkey **);
157
int	 sshkey_fromb(struct sshbuf *, struct sshkey **);
158
int	 sshkey_froms(struct sshbuf *, struct sshkey **);
158
int	 sshkey_froms(struct sshbuf *, struct sshkey **);
159
int	 sshkey_to_blob(const struct sshkey *, u_char **, size_t *);
159
int	 sshkey_to_blob(const struct sshkey *, u_char **, size_t *);
160
int	 sshkey_to_base64(const struct sshkey *, char **);
160
int	 sshkey_putb(const struct sshkey *, struct sshbuf *);
161
int	 sshkey_putb(const struct sshkey *, struct sshbuf *);
161
int	 sshkey_puts(const struct sshkey *, struct sshbuf *);
162
int	 sshkey_puts(const struct sshkey *, struct sshbuf *);
162
int	 sshkey_plain_to_blob(const struct sshkey *, u_char **, size_t *);
163
int	 sshkey_plain_to_blob(const struct sshkey *, u_char **, size_t *);

Return to bug 2081