mirror of
https://git.libssh.org/projects/libssh.git
synced 2026-02-04 12:20:42 +09:00
Added support for ~/.ssh/config.
This commit is contained in:
@@ -401,6 +401,7 @@ LIBSSH_API int ssh_options_set_dsa_server_key(SSH_OPTIONS *opt, const char *dsak
|
||||
LIBSSH_API int ssh_options_set_rsa_server_key(SSH_OPTIONS *opt, const char *rsakey);
|
||||
LIBSSH_API int ssh_options_set_auth_callback(SSH_OPTIONS *opt, ssh_auth_callback cb,
|
||||
void *userdata);
|
||||
LIBSSH_API int ssh_options_parse_config(SSH_OPTIONS *opt, const char *filename);
|
||||
|
||||
|
||||
/* buffer.c */
|
||||
|
||||
@@ -634,6 +634,9 @@ void ssh_cleanup(SSH_SESSION *session);
|
||||
int ssh_send_banner(SSH_SESSION *session, int is_server);
|
||||
char *ssh_get_banner(SSH_SESSION *session);
|
||||
|
||||
/* config.c */
|
||||
int ssh_config_parse_file(ssh_options opt, const char *filename);
|
||||
|
||||
/* errors.c */
|
||||
void ssh_set_error(void *error, int code, const char *descr, ...) PRINTF_ATTRIBUTE(3, 4);
|
||||
|
||||
|
||||
@@ -74,6 +74,7 @@ set(libssh_SRCS
|
||||
buffer.c
|
||||
channels.c
|
||||
client.c
|
||||
config.c
|
||||
connect.c
|
||||
crc32.c
|
||||
crypt.c
|
||||
|
||||
305
libssh/config.c
Normal file
305
libssh/config.c
Normal file
@@ -0,0 +1,305 @@
|
||||
/*
|
||||
* config.c - parse the ssh config file
|
||||
*
|
||||
* This file is part of the SSH Library
|
||||
*
|
||||
* Copyright (c) 2009 by Andreas Schneider <mail@cynapses.org>
|
||||
*
|
||||
* The SSH Library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* The SSH Library is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with the SSH Library; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
* MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "libssh/priv.h"
|
||||
|
||||
enum ssh_config_opcode_e {
|
||||
SOC_UNSUPPORTED = -1,
|
||||
SOC_HOST,
|
||||
SOC_HOSTNAME,
|
||||
SOC_PORT,
|
||||
SOC_USERNAME,
|
||||
SOC_IDENTITY,
|
||||
SOC_CIPHER,
|
||||
SOC_CIPHERS,
|
||||
SOC_COMPRESSION,
|
||||
SOC_TIMEOUT,
|
||||
SOC_PROTOCOL
|
||||
};
|
||||
|
||||
struct ssh_config_keyword_table_s {
|
||||
const char *name;
|
||||
enum ssh_config_opcode_e opcode;
|
||||
};
|
||||
|
||||
static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = {
|
||||
{ "host", SOC_HOST },
|
||||
{ "hostname", SOC_HOSTNAME },
|
||||
{ "port", SOC_PORT },
|
||||
{ "user", SOC_USERNAME },
|
||||
{ "identityfile", SOC_IDENTITY },
|
||||
{ "cipher", SOC_CIPHER },
|
||||
{ "ciphers", SOC_CIPHERS },
|
||||
{ "compression", SOC_COMPRESSION },
|
||||
{ "connecttimeout", SOC_TIMEOUT },
|
||||
{ "protocol", SOC_PROTOCOL },
|
||||
{ NULL, SOC_UNSUPPORTED }
|
||||
};
|
||||
|
||||
static enum ssh_config_opcode_e ssh_config_get_opcode(char *keyword) {
|
||||
int i;
|
||||
|
||||
for (i = 0; ssh_config_keyword_table[i].name != NULL; i++) {
|
||||
if (strcasecmp(keyword, ssh_config_keyword_table[i].name) == 0) {
|
||||
return ssh_config_keyword_table[i].opcode;
|
||||
}
|
||||
}
|
||||
|
||||
return SOC_UNSUPPORTED;
|
||||
}
|
||||
|
||||
static char *ssh_config_get_token(char **str) {
|
||||
register char *c;
|
||||
char *r;
|
||||
|
||||
/* Ignore leading spaces */
|
||||
for (c = *str; *c; c++) {
|
||||
if (! isblank(*c)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (*c == '\"') {
|
||||
for (r = ++c; *c; c++) {
|
||||
if (*c == '\"') {
|
||||
*c = '\0';
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (r = c; *c; c++) {
|
||||
if (isblank(*c)) {
|
||||
*c = '\0';
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
*str = c + 1;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int ssh_config_get_int(char **str, int notfound) {
|
||||
char *p, *endp;
|
||||
int i;
|
||||
|
||||
p = ssh_config_get_token(str);
|
||||
if (p && *p) {
|
||||
i = strtol(p, &endp, 10);
|
||||
if (p == endp) {
|
||||
return notfound;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
return notfound;
|
||||
}
|
||||
|
||||
static const char *ssh_config_get_str(char **str, const char *def) {
|
||||
char *p;
|
||||
|
||||
p = ssh_config_get_token(str);
|
||||
if (p && *p) {
|
||||
return p;
|
||||
}
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
static int ssh_config_get_yesno(char **str, int notfound) {
|
||||
const char *p;
|
||||
|
||||
p = ssh_config_get_str(str, NULL);
|
||||
if (p == NULL) {
|
||||
return notfound;
|
||||
}
|
||||
|
||||
if (strncasecmp(p, "yes", 3) == 0) {
|
||||
return 1;
|
||||
} else if (strncasecmp(p, "no", 2) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return notfound;
|
||||
}
|
||||
|
||||
static int ssh_config_parse_line(ssh_options opt, const char *line,
|
||||
unsigned int count, int *parsing) {
|
||||
enum ssh_config_opcode_e opcode;
|
||||
const char *p;
|
||||
char *s, *x;
|
||||
char *keyword;
|
||||
size_t len;
|
||||
int i;
|
||||
|
||||
x = s = strdup(line);
|
||||
if (s == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Remove trailing spaces */
|
||||
for (len = strlen(s) - 1; len > 0; len--) {
|
||||
if (! isspace(s[len])) {
|
||||
break;
|
||||
}
|
||||
s[len] = '\0';
|
||||
}
|
||||
|
||||
keyword = ssh_config_get_token(&s);
|
||||
if (keyword == NULL || *keyword == '#' ||
|
||||
*keyword == '\0' || *keyword == '\n') {
|
||||
SAFE_FREE(x);
|
||||
return 0;
|
||||
}
|
||||
|
||||
opcode = ssh_config_get_opcode(keyword);
|
||||
|
||||
switch (opcode) {
|
||||
case SOC_HOST:
|
||||
*parsing = 0;
|
||||
for (p = ssh_config_get_str(&s, NULL); p && *p;
|
||||
p = ssh_config_get_str(&s, NULL)) {
|
||||
if (match_hostname(opt->host, p, strlen(p))) {
|
||||
*parsing = 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SOC_HOSTNAME:
|
||||
p = ssh_config_get_str(&s, NULL);
|
||||
if (p && *parsing) {
|
||||
ssh_options_set_host(opt, p);
|
||||
}
|
||||
break;
|
||||
case SOC_PORT:
|
||||
i = ssh_config_get_int(&s, -1);
|
||||
if (i > 0 && *parsing) {
|
||||
ssh_options_set_port(opt, (unsigned int) i);
|
||||
}
|
||||
break;
|
||||
case SOC_USERNAME:
|
||||
p = ssh_config_get_str(&s, NULL);
|
||||
if (p && *parsing) {
|
||||
ssh_options_set_username(opt, p);
|
||||
}
|
||||
break;
|
||||
case SOC_IDENTITY:
|
||||
p = ssh_config_get_str(&s, NULL);
|
||||
if (p && *parsing) {
|
||||
ssh_options_set_identity(opt, p);
|
||||
}
|
||||
break;
|
||||
case SOC_CIPHERS:
|
||||
/* TODO */
|
||||
break;
|
||||
case SOC_COMPRESSION:
|
||||
i = ssh_config_get_yesno(&s, -1);
|
||||
if (i >= 0 && *parsing) {
|
||||
if (i) {
|
||||
ssh_options_set_wanted_algos(opt, SSH_COMP_C_S, "zlib");
|
||||
ssh_options_set_wanted_algos(opt, SSH_COMP_S_C, "zlib");
|
||||
} else {
|
||||
ssh_options_set_wanted_algos(opt, SSH_COMP_C_S, "none");
|
||||
ssh_options_set_wanted_algos(opt, SSH_COMP_S_C, "none");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SOC_PROTOCOL:
|
||||
p = ssh_config_get_str(&s, NULL);
|
||||
if (p && *parsing) {
|
||||
char *a, *b;
|
||||
b = strdup(p);
|
||||
if (b == NULL) {
|
||||
SAFE_FREE(x);
|
||||
return -1;
|
||||
}
|
||||
ssh_options_allow_ssh1(opt, 0);
|
||||
ssh_options_allow_ssh2(opt, 0);
|
||||
|
||||
for (a = strtok(b, ","); a; a = strtok(NULL, ",")) {
|
||||
switch (atoi(a)) {
|
||||
case 1:
|
||||
ssh_options_allow_ssh1(opt, 1);
|
||||
break;
|
||||
case 2:
|
||||
ssh_options_allow_ssh2(opt, 1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
SAFE_FREE(b);
|
||||
}
|
||||
break;
|
||||
case SOC_TIMEOUT:
|
||||
i = ssh_config_get_int(&s, -1);
|
||||
if (i >= 0 && *parsing) {
|
||||
ssh_options_set_timeout(opt, (long) i, 0);
|
||||
}
|
||||
break;
|
||||
case SOC_UNSUPPORTED:
|
||||
fprintf(stderr, "Unsupported option: %s, line: %d\n", keyword, count);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "ERROR - unimplemented opcode: %d\n", opcode);
|
||||
SAFE_FREE(x);
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
|
||||
SAFE_FREE(x);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ssh_config_parse_file */
|
||||
int ssh_config_parse_file(ssh_options opt, const char *filename) {
|
||||
char line[1024] = {0};
|
||||
unsigned int count = 0;
|
||||
FILE *f;
|
||||
int parsing;
|
||||
|
||||
if ((f = fopen(filename, "r")) == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (opt->log_verbosity) {
|
||||
fprintf(stderr, "Reading configuration data from %s\n", filename);
|
||||
}
|
||||
|
||||
parsing = 1;
|
||||
while (fgets(line, sizeof(line), f)) {
|
||||
count++;
|
||||
if (ssh_config_parse_line(opt, line, count, &parsing) < 0) {
|
||||
fclose(f);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
@@ -1018,5 +1018,40 @@ int ssh_options_set_auth_callback(SSH_OPTIONS *opt, ssh_auth_callback cb,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Parse the ssh config file.
|
||||
*
|
||||
* This must be the last call of all options, it may overwrite options which
|
||||
* are already set. It requires at least the hostname to be set.
|
||||
*
|
||||
* @param opt The options structure to use.
|
||||
*
|
||||
* @param filename The options file to use, if NULL the default
|
||||
* ~/.ssh/config will be used.
|
||||
*
|
||||
* @return 0 on success, < 0 on error.
|
||||
*/
|
||||
int ssh_options_parse_config(ssh_options opt, const char *filename) {
|
||||
char buffer[1024] = {0};
|
||||
|
||||
if (opt == NULL || opt->host == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (opt->ssh_dir == NULL) {
|
||||
if (ssh_options_default_ssh_dir(opt) < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* set default filename */
|
||||
if (filename == NULL) {
|
||||
snprintf(buffer, 1024, "%s/config", opt->ssh_dir);
|
||||
filename = buffer;
|
||||
}
|
||||
|
||||
return ssh_config_parse_file(opt, filename);
|
||||
}
|
||||
|
||||
/** @} */
|
||||
/* vim: set ts=2 sw=2 et cindent: */
|
||||
|
||||
Reference in New Issue
Block a user