Edit

IABSD.fr/src/usr.bin/ssh/authfile.c

Branch :

  • Show log

    Commit

  • Author : jsg
    Date : 2026-02-14 00:18:34
    Hash : dd4215a7
    Message : remove unneeded includes; ok dtucker@

  • usr.bin/ssh/authfile.c
  • /* $OpenBSD: authfile.c,v 1.149 2026/02/14 00:18:34 jsg Exp $ */
    /*
     * Copyright (c) 2000, 2013 Markus Friedl.  All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     * 1. Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     * 2. Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the distribution.
     *
     * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     */
    
    
    #include <sys/types.h>
    #include <sys/stat.h>
    
    #include <errno.h>
    #include <fcntl.h>
    #include <stdio.h>
    #include <stdarg.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    
    #include "log.h"
    #include "authfile.h"
    #include "sshkey.h"
    #include "sshbuf.h"
    #include "ssherr.h"
    #include "krl.h"
    
    /* Save a key blob to a file */
    static int
    sshkey_save_private_blob(struct sshbuf *keybuf, const char *filename)
    {
    	int r;
    	mode_t omask;
    
    	omask = umask(077);
    	r = sshbuf_write_file(filename, keybuf);
    	umask(omask);
    	return r;
    }
    
    int
    sshkey_save_private(struct sshkey *key, const char *filename,
        const char *passphrase, const char *comment,
        int format, const char *openssh_format_cipher, int openssh_format_rounds)
    {
    	struct sshbuf *keyblob = NULL;
    	int r;
    
    	if ((keyblob = sshbuf_new()) == NULL)
    		return SSH_ERR_ALLOC_FAIL;
    	if ((r = sshkey_private_to_fileblob(key, keyblob, passphrase, comment,
    	    format, openssh_format_cipher, openssh_format_rounds)) != 0)
    		goto out;
    	if ((r = sshkey_save_private_blob(keyblob, filename)) != 0)
    		goto out;
    	r = 0;
     out:
    	sshbuf_free(keyblob);
    	return r;
    }
    
    /* XXX remove error() calls from here? */
    int
    sshkey_perm_ok(int fd, const char *filename)
    {
    	struct stat st;
    
    	if (fstat(fd, &st) == -1)
    		return SSH_ERR_SYSTEM_ERROR;
    	/*
    	 * if a key owned by the user is accessed, then we check the
    	 * permissions of the file. if the key owned by a different user,
    	 * then we don't care.
    	 */
    	if ((st.st_uid == getuid()) && (st.st_mode & 077) != 0) {
    		error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
    		error("@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @");
    		error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
    		error("Permissions 0%3.3o for '%s' are too open.",
    		    (u_int)st.st_mode & 0777, filename);
    		error("It is required that your private key files are NOT accessible by others.");
    		error("This private key will be ignored.");
    		return SSH_ERR_KEY_BAD_PERMISSIONS;
    	}
    	return 0;
    }
    
    int
    sshkey_load_private_type(int type, const char *filename, const char *passphrase,
        struct sshkey **keyp, char **commentp)
    {
    	int fd, r;
    
    	if (keyp != NULL)
    		*keyp = NULL;
    	if (commentp != NULL)
    		*commentp = NULL;
    
    	if ((fd = open(filename, O_RDONLY)) == -1)
    		return SSH_ERR_SYSTEM_ERROR;
    
    	r = sshkey_perm_ok(fd, filename);
    	if (r != 0)
    		goto out;
    
    	r = sshkey_load_private_type_fd(fd, type, passphrase, keyp, commentp);
     out:
    	close(fd);
    	return r;
    }
    
    int
    sshkey_load_private(const char *filename, const char *passphrase,
        struct sshkey **keyp, char **commentp)
    {
    	return sshkey_load_private_type(KEY_UNSPEC, filename, passphrase,
    	    keyp, commentp);
    }
    
    int
    sshkey_load_private_type_fd(int fd, int type, const char *passphrase,
        struct sshkey **keyp, char **commentp)
    {
    	struct sshbuf *buffer = NULL;
    	int r;
    
    	if (keyp != NULL)
    		*keyp = NULL;
    	if ((r = sshbuf_load_fd(fd, &buffer)) != 0 ||
    	    (r = sshkey_parse_private_fileblob_type(buffer, type,
    	    passphrase, keyp, commentp)) != 0)
    		goto out;
    
    	/* success */
    	r = 0;
     out:
    	sshbuf_free(buffer);
    	return r;
    }
    
    /* Load a pubkey from the unencrypted envelope of a new-format private key */
    static int
    sshkey_load_pubkey_from_private(const char *filename, struct sshkey **pubkeyp)
    {
    	struct sshbuf *buffer = NULL;
    	struct sshkey *pubkey = NULL;
    	int r, fd;
    
    	if (pubkeyp != NULL)
    		*pubkeyp = NULL;
    
    	if ((fd = open(filename, O_RDONLY)) == -1)
    		return SSH_ERR_SYSTEM_ERROR;
    	if ((r = sshbuf_load_fd(fd, &buffer)) != 0 ||
    	    (r = sshkey_parse_pubkey_from_private_fileblob_type(buffer,
    	    KEY_UNSPEC, &pubkey)) != 0)
    		goto out;
    	/* success */
    	if (pubkeyp != NULL) {
    		*pubkeyp = pubkey;
    		pubkey = NULL;
    	}
    	r = 0;
     out:
    	close(fd);
    	sshbuf_free(buffer);
    	sshkey_free(pubkey);
    	return r;
    }
    
    static int
    sshkey_try_load_public(struct sshkey **kp, const char *filename,
        char **commentp)
    {
    	FILE *f;
    	char *line = NULL, *cp;
    	size_t linesize = 0;
    	int r;
    	struct sshkey *k = NULL;
    
    	if (kp == NULL)
    		return SSH_ERR_INVALID_ARGUMENT;
    	*kp = NULL;
    	if (commentp != NULL)
    		*commentp = NULL;
    	if ((f = fopen(filename, "r")) == NULL)
    		return SSH_ERR_SYSTEM_ERROR;
    	if ((k = sshkey_new(KEY_UNSPEC)) == NULL) {
    		fclose(f);
    		return SSH_ERR_ALLOC_FAIL;
    	}
    	while (getline(&line, &linesize, f) != -1) {
    		cp = line;
    		switch (*cp) {
    		case '#':
    		case '\n':
    		case '\0':
    			continue;
    		}
    		/* Abort loading if this looks like a private key */
    		if (strncmp(cp, "-----BEGIN", 10) == 0 ||
    		    strcmp(cp, "SSH PRIVATE KEY FILE") == 0)
    			break;
    		/* Skip leading whitespace. */
    		for (; *cp && (*cp == ' ' || *cp == '\t'); cp++)
    			;
    		if (*cp) {
    			if ((r = sshkey_read(k, &cp)) == 0) {
    				cp[strcspn(cp, "\r\n")] = '\0';
    				if (commentp) {
    					*commentp = strdup(*cp ?
    					    cp : filename);
    					if (*commentp == NULL)
    						r = SSH_ERR_ALLOC_FAIL;
    				}
    				/* success */
    				*kp = k;
    				free(line);
    				fclose(f);
    				return r;
    			}
    		}
    	}
    	free(k);
    	free(line);
    	fclose(f);
    	return SSH_ERR_INVALID_FORMAT;
    }
    
    /* load public key from any pubkey file */
    int
    sshkey_load_public(const char *filename, struct sshkey **keyp, char **commentp)
    {
    	char *pubfile = NULL;
    	int r, oerrno;
    
    	if (keyp != NULL)
    		*keyp = NULL;
    	if (commentp != NULL)
    		*commentp = NULL;
    
    	if ((r = sshkey_try_load_public(keyp, filename, commentp)) == 0)
    		goto out;
    
    	/* try .pub suffix */
    	if (asprintf(&pubfile, "%s.pub", filename) == -1)
    		return SSH_ERR_ALLOC_FAIL;
    	if ((r = sshkey_try_load_public(keyp, pubfile, commentp)) == 0)
    		goto out;
    
    	/* finally, try to extract public key from private key file */
    	if ((r = sshkey_load_pubkey_from_private(filename, keyp)) == 0)
    		goto out;
    
    	/* Pretend we couldn't find the key */
    	r = SSH_ERR_SYSTEM_ERROR;
    	errno = ENOENT;
    
     out:
    	oerrno = errno;
    	free(pubfile);
    	errno = oerrno;
    	return r;
    }
    
    /* Load the certificate associated with the named private key */
    int
    sshkey_load_cert(const char *filename, struct sshkey **keyp)
    {
    	struct sshkey *pub = NULL;
    	char *file = NULL;
    	int r = SSH_ERR_INTERNAL_ERROR;
    
    	if (keyp != NULL)
    		*keyp = NULL;
    
    	if (asprintf(&file, "%s-cert.pub", filename) == -1)
    		return SSH_ERR_ALLOC_FAIL;
    
    	r = sshkey_try_load_public(keyp, file, NULL);
    	free(file);
    	sshkey_free(pub);
    	return r;
    }
    
    /* Load private key and certificate */
    int
    sshkey_load_private_cert(int type, const char *filename, const char *passphrase,
        struct sshkey **keyp)
    {
    	struct sshkey *key = NULL, *cert = NULL;
    	int r;
    
    	if (keyp != NULL)
    		*keyp = NULL;
    
    	switch (type) {
    #ifdef WITH_OPENSSL
    	case KEY_RSA:
    	case KEY_ECDSA:
    #endif /* WITH_OPENSSL */
    	case KEY_ED25519:
    	case KEY_UNSPEC:
    		break;
    	default:
    		return SSH_ERR_KEY_TYPE_UNKNOWN;
    	}
    
    	if ((r = sshkey_load_private_type(type, filename,
    	    passphrase, &key, NULL)) != 0 ||
    	    (r = sshkey_load_cert(filename, &cert)) != 0)
    		goto out;
    
    	/* Make sure the private key matches the certificate */
    	if (sshkey_equal_public(key, cert) == 0) {
    		r = SSH_ERR_KEY_CERT_MISMATCH;
    		goto out;
    	}
    
    	if ((r = sshkey_to_certified(key)) != 0 ||
    	    (r = sshkey_cert_copy(cert, key)) != 0)
    		goto out;
    	r = 0;
    	if (keyp != NULL) {
    		*keyp = key;
    		key = NULL;
    	}
     out:
    	sshkey_free(key);
    	sshkey_free(cert);
    	return r;
    }
    
    /*
     * Returns success if the specified "key" is listed in the file "filename",
     * SSH_ERR_KEY_NOT_FOUND: if the key is not listed or another error.
     * If "strict_type" is set then the key type must match exactly,
     * otherwise a comparison that ignores certificate data is performed.
     * If "check_ca" is set and "key" is a certificate, then its CA key is
     * also checked and sshkey_in_file() will return success if either is found.
     */
    int
    sshkey_in_file(struct sshkey *key, const char *filename, int strict_type,
        int check_ca)
    {
    	FILE *f;
    	char *line = NULL, *cp;
    	size_t linesize = 0;
    	int r = 0;
    	struct sshkey *pub = NULL;
    
    	int (*sshkey_compare)(const struct sshkey *, const struct sshkey *) =
    	    strict_type ?  sshkey_equal : sshkey_equal_public;
    
    	if ((f = fopen(filename, "r")) == NULL)
    		return SSH_ERR_SYSTEM_ERROR;
    
    	while (getline(&line, &linesize, f) != -1) {
    		sshkey_free(pub);
    		pub = NULL;
    		cp = line;
    
    		/* Skip leading whitespace. */
    		for (; *cp && (*cp == ' ' || *cp == '\t'); cp++)
    			;
    
    		/* Skip comments and empty lines */
    		switch (*cp) {
    		case '#':
    		case '\n':
    		case '\0':
    			continue;
    		}
    
    		if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) {
    			r = SSH_ERR_ALLOC_FAIL;
    			goto out;
    		}
    		switch (r = sshkey_read(pub, &cp)) {
    		case 0:
    			break;
    		case SSH_ERR_KEY_LENGTH:
    			continue;
    		default:
    			goto out;
    		}
    		if (sshkey_compare(key, pub) ||
    		    (check_ca && sshkey_is_cert(key) &&
    		    sshkey_compare(key->cert->signature_key, pub))) {
    			r = 0;
    			goto out;
    		}
    	}
    	r = SSH_ERR_KEY_NOT_FOUND;
     out:
    	free(line);
    	sshkey_free(pub);
    	fclose(f);
    	return r;
    }
    
    /*
     * Checks whether the specified key is revoked, returning 0 if not,
     * SSH_ERR_KEY_REVOKED if it is or another error code if something
     * unexpected happened.
     * This will check both the key and, if it is a certificate, its CA key too.
     * "revoked_keys_file" may be a KRL or a one-per-line list of public keys.
     */
    int
    sshkey_check_revoked(struct sshkey *key, const char *revoked_keys_file)
    {
    	int r;
    
    	r = ssh_krl_file_contains_key(revoked_keys_file, key);
    	/* If this was not a KRL to begin with then continue below */
    	if (r != SSH_ERR_KRL_BAD_MAGIC)
    		return r;
    
    	/*
    	 * If the file is not a KRL or we can't handle KRLs then attempt to
    	 * parse the file as a flat list of keys.
    	 */
    	switch ((r = sshkey_in_file(key, revoked_keys_file, 0, 1))) {
    	case 0:
    		/* Key found => revoked */
    		return SSH_ERR_KEY_REVOKED;
    	case SSH_ERR_KEY_NOT_FOUND:
    		/* Key not found => not revoked */
    		return 0;
    	default:
    		/* Some other error occurred */
    		return r;
    	}
    }
    
    /*
     * Advanced *cpp past the end of key options, defined as the first unquoted
     * whitespace character. Returns 0 on success or -1 on failure (e.g.
     * unterminated quotes).
     */
    int
    sshkey_advance_past_options(char **cpp)
    {
    	char *cp = *cpp;
    	int quoted = 0;
    
    	for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
    		if (*cp == '\\' && cp[1] == '"')
    			cp++;	/* Skip both */
    		else if (*cp == '"')
    			quoted = !quoted;
    	}
    	*cpp = cp;
    	/* return failure for unterminated quotes */
    	return (*cp == '\0' && quoted) ? -1 : 0;
    }
    
    /* Save a public key */
    int
    sshkey_save_public(const struct sshkey *key, const char *path,
        const char *comment)
    {
    	int fd, oerrno;
    	FILE *f = NULL;
    	int r = SSH_ERR_INTERNAL_ERROR;
    
    	if ((fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1)
    		return SSH_ERR_SYSTEM_ERROR;
    	if ((f = fdopen(fd, "w")) == NULL) {
    		r = SSH_ERR_SYSTEM_ERROR;
    		close(fd);
    		goto fail;
    	}
    	if ((r = sshkey_write(key, f)) != 0)
    		goto fail;
    	fprintf(f, " %s\n", comment);
    	if (ferror(f)) {
    		r = SSH_ERR_SYSTEM_ERROR;
    		goto fail;
    	}
    	if (fclose(f) != 0) {
    		r = SSH_ERR_SYSTEM_ERROR;
    		f = NULL;
     fail:
    		if (f != NULL) {
    			oerrno = errno;
    			fclose(f);
    			errno = oerrno;
    		}
    		return r;
    	}
    	return 0;
    }