svnno****@sourc*****
svnno****@sourc*****
2015年 5月 6日 (水) 03:08:25 JST
Revision: 5849 http://sourceforge.jp/projects/ttssh2/scm/svn/commits/5849 Author: yutakapon Date: 2015-05-06 03:08:23 +0900 (Wed, 06 May 2015) Log Message: ----------- チケット #35047 SSH サーバホスト公開鍵の自動更新 ・UpdateHostkeys エントリに"2"(ASK)を追加した。 ・known_hosts ファイルの更新処理を追加した。 Ticket Links: ------------ http://sourceforge.jp/projects/ttssh2/tracker/detail/35047 Modified Paths: -------------- trunk/installer/release/TERATERM.INI trunk/ttssh2/ttxssh/hosts.c trunk/ttssh2/ttxssh/hosts.h trunk/ttssh2/ttxssh/key.c trunk/ttssh2/ttxssh/ttxssh.c trunk/ttssh2/ttxssh/ttxssh.h -------------- next part -------------- Modified: trunk/installer/release/TERATERM.INI =================================================================== --- trunk/installer/release/TERATERM.INI 2015-05-04 17:46:30 UTC (rev 5848) +++ trunk/installer/release/TERATERM.INI 2015-05-05 18:08:23 UTC (rev 5849) @@ -833,6 +833,7 @@ ; Host key rotation support (derived from OpenSSH 6.8) ; 0 ... Disabled ; 1 ... Enabled +; 2 ... Enabled with User's confirmation UpdateHostkeys=0 Modified: trunk/ttssh2/ttxssh/hosts.c =================================================================== --- trunk/ttssh2/ttxssh/hosts.c 2015-05-04 17:46:30 UTC (rev 5848) +++ trunk/ttssh2/ttxssh/hosts.c 2015-05-05 18:08:23 UTC (rev 5849) @@ -1231,6 +1231,103 @@ return result; } +static char FAR *format_specified_host_key(Key *key, char *hostname, unsigned short tcpport) +{ + int host_len = strlen(hostname); + char *result = NULL; + int index; + ssh_keytype type = key->type; + + switch (type) { + case KEY_RSA1: + { + int result_len = host_len + 50 + 8 + + get_ushort16_MSBfirst(key->exp) / 3 + + get_ushort16_MSBfirst(key->mod) / 3; + result = (char FAR *) malloc(result_len); + + if (tcpport == 22) { + strncpy_s(result, result_len, hostname, _TRUNCATE); + index = host_len; + } + else { + _snprintf_s(result, result_len, _TRUNCATE, "[%s]:%d", + hostname, + tcpport); + index = strlen(result); + } + + _snprintf_s(result + index, result_len - host_len, _TRUNCATE, + " %d ", key->bits); + index += strlen(result + index); + index += print_mp_int(result + index, key->exp); + result[index] = ' '; + index++; + index += print_mp_int(result + index, key->mod); + strncpy_s(result + index, result_len - index, " \r\n", _TRUNCATE); + + break; + } + + case KEY_RSA: + case KEY_DSA: + case KEY_ECDSA256: + case KEY_ECDSA384: + case KEY_ECDSA521: + case KEY_ED25519: + { + //Key *key = &pvar->hosts_state.hostkey; + char *blob = NULL; + int blen, uulen, msize; + char *uu = NULL; + int n; + + key_to_blob(key, &blob, &blen); + uulen = 2 * blen; + uu = malloc(uulen); + if (uu == NULL) { + goto error; + } + n = uuencode(blob, blen, uu, uulen); + if (n > 0) { + msize = host_len + 50 + uulen; + result = malloc(msize); + if (result == NULL) { + goto error; + } + + // setup + if (tcpport == 22) { + _snprintf_s(result, msize, _TRUNCATE, "%s %s %s\r\n", + hostname, + get_sshname_from_key(key), + uu); + } + else { + _snprintf_s(result, msize, _TRUNCATE, "[%s]:%d %s %s\r\n", + hostname, + tcpport, + get_sshname_from_key(key), + uu); + } + } + error: + if (blob != NULL) + free(blob); + if (uu != NULL) + free(uu); + + break; + } + + default: + return NULL; + + } + + return result; +} + static void add_host_key(PTInstVar pvar) { char FAR *name = NULL; @@ -1283,6 +1380,66 @@ } } +// \x8Ew\x92肵\x82\xBD\x83L\x81[\x82\xF0 known_hosts \x82ɒlj\xC1\x82\xB7\x82\xE9\x81B +void HOSTS_add_host_key(PTInstVar pvar, Key *key) +{ + char FAR *name = NULL; + char *hostname; + unsigned short tcpport; + + hostname = pvar->ssh_state.hostname; + tcpport = pvar->ssh_state.tcpport; + + if (pvar->hosts_state.file_names != NULL) + name = pvar->hosts_state.file_names[0]; + + if (name == NULL || name[0] == 0) { + UTIL_get_lang_msg("MSG_HOSTS_FILE_UNSPECIFY_ERROR", pvar, + "The host and its key cannot be added, because no known-hosts file has been specified.\n" + "Restart Tera Term and specify a read/write known-hosts file in the TTSSH Setup dialog box."); + notify_nonfatal_error(pvar, pvar->ts->UIMsg); + } + else { + char FAR *keydata = format_specified_host_key(key, hostname, tcpport); + int length = strlen(keydata); + int fd; + int amount_written; + int close_result; + char buf[FILENAME_MAX]; + + get_teraterm_dir_relative_name(buf, sizeof(buf), name); + fd = _open(buf, + _O_APPEND | _O_CREAT | _O_WRONLY | _O_SEQUENTIAL | _O_BINARY, + _S_IREAD | _S_IWRITE); + if (fd == -1) { + if (errno == EACCES) { + UTIL_get_lang_msg("MSG_HOSTS_WRITE_EACCES_ERROR", pvar, + "An error occurred while trying to write the host key.\n" + "You do not have permission to write to the known-hosts file."); + notify_nonfatal_error(pvar, pvar->ts->UIMsg); + } + else { + UTIL_get_lang_msg("MSG_HOSTS_WRITE_ERROR", pvar, + "An error occurred while trying to write the host key.\n" + "The host key could not be written."); + notify_nonfatal_error(pvar, pvar->ts->UIMsg); + } + return; + } + + amount_written = _write(fd, keydata, length); + free(keydata); + close_result = _close(fd); + + if (amount_written != length || close_result == -1) { + UTIL_get_lang_msg("MSG_HOSTS_WRITE_ERROR", pvar, + "An error occurred while trying to write the host key.\n" + "The host key could not be written."); + notify_nonfatal_error(pvar, pvar->ts->UIMsg); + } + } +} + static char FAR *copy_mp_int(char FAR * num) { int len = (get_ushort16_MSBfirst(num) + 7) / 8 + 2; @@ -1513,6 +1670,225 @@ } } + +void HOSTS_delete_all_hostkeys(PTInstVar pvar) +{ + char FAR *name = pvar->hosts_state.file_names[0]; + char *hostname; + unsigned short tcpport; + + hostname = pvar->ssh_state.hostname; + tcpport = pvar->ssh_state.tcpport; + + if (name == NULL || name[0] == 0) { + UTIL_get_lang_msg("MSG_HOSTS_FILE_UNSPECIFY_ERROR", pvar, + "The host and its key cannot be added, because no known-hosts file has been specified.\n" + "Restart Tera Term and specify a read/write known-hosts file in the TTSSH Setup dialog box."); + notify_nonfatal_error(pvar, pvar->ts->UIMsg); + } + else { + Key key; // \x90ڑ\xB1\x92\x86\x82̃z\x83X\x83g\x82̃L\x81[ + Key *key_freed; + int length; + char filename[MAX_PATH]; + char tmp[L_tmpnam]; + int fd; + int amount_written = 0; + int close_result; + int data_index = 0; + char buf[FILENAME_MAX]; + + // \x8F\x91\x82\xAB\x8D\x9E\x82݈ꎞ\x83t\x83@\x83C\x83\x8B\x82\xF0\x8AJ\x82\xAD + _getcwd(filename, sizeof(filename)); + tmpnam_s(tmp, sizeof(tmp)); + strcat_s(filename, sizeof(filename), tmp); + fd = _open(filename, + _O_CREAT | _O_WRONLY | _O_SEQUENTIAL | _O_BINARY | _O_TRUNC, + _S_IREAD | _S_IWRITE); + + if (fd == -1) { + if (errno == EACCES) { + UTIL_get_lang_msg("MSG_HOSTS_WRITE_EACCES_ERROR", pvar, + "An error occurred while trying to write the host key.\n" + "You do not have permission to write to the known-hosts file."); + notify_nonfatal_error(pvar, pvar->ts->UIMsg); + } + else { + UTIL_get_lang_msg("MSG_HOSTS_WRITE_ERROR", pvar, + "An error occurred while trying to write the host key.\n" + "The host key could not be written."); + notify_nonfatal_error(pvar, pvar->ts->UIMsg); + } + return; + } + + // \x90ڑ\xB1\x92\x86\x82̃T\x81[\x83o\x82̃L\x81[\x82\xF0\x93ǂݍ\x9E\x82\xDE + memset(&key, 0, sizeof(key)); + switch (pvar->hosts_state.hostkey.type) { + case KEY_RSA1: // SSH1 + key.type = KEY_RSA1; + key.bits = pvar->hosts_state.hostkey.bits; + key.exp = copy_mp_int(pvar->hosts_state.hostkey.exp); + key.mod = copy_mp_int(pvar->hosts_state.hostkey.mod); + break; + case KEY_RSA: // SSH2 RSA + key.type = KEY_RSA; + key.rsa = duplicate_RSA(pvar->hosts_state.hostkey.rsa); + break; + case KEY_DSA: // SSH2 DSA + key.type = KEY_DSA; + key.dsa = duplicate_DSA(pvar->hosts_state.hostkey.dsa); + break; + case KEY_ECDSA256: + case KEY_ECDSA384: + case KEY_ECDSA521: + key.type = pvar->hosts_state.hostkey.type; + key.ecdsa = EC_KEY_dup(pvar->hosts_state.hostkey.ecdsa); + break; + case KEY_ED25519: + key.type = pvar->hosts_state.hostkey.type; + key.ed25519_pk = duplicate_ED25519_PK(pvar->hosts_state.hostkey.ed25519_pk); + break; + } + + // \x83t\x83@\x83C\x83\x8B\x82\xA9\x82\xE7\x93ǂݍ\x9E\x82\xDE + begin_read_host_files(pvar, 0); + do { + int host_index = 0; + int matched = 0; + int keybits = 0; + char FAR *data; + int do_write = 0; + length = amount_written = 0; + + if (!read_host_key(pvar, pvar->ssh_state.hostname, pvar->ssh_state.tcpport, 0, 1)) { + break; + } + + if (data_index == pvar->hosts_state.file_data_index) { + // index \x82\xAA\x90i\x82܂Ȃ\xA2 == \x8DŌ\xE3\x82܂œǂ\xF1\x82\xBE + break; + } + + data = pvar->hosts_state.file_data + data_index; + host_index = eat_spaces(data); + + if (data[host_index] == '#') { + do_write = 1; + } + else { + // \x83z\x83X\x83g\x82̏ƍ\x87 + host_index--; + do { + int negated; + int bracketed; + char *end_bracket; + int host_matched = 0; + unsigned short keyfile_port = 22; + + host_index++; + negated = data[host_index] == '!'; + + if (negated) { + host_index++; + bracketed = data[host_index] == '['; + if (bracketed) { + end_bracket = strstr(data + host_index + 1, "]:"); + if (end_bracket != NULL) { + *end_bracket = ' '; + host_index++; + } + } + host_matched = match_pattern(data + host_index, pvar->ssh_state.hostname); + if (bracketed && end_bracket != NULL) { + *end_bracket = ']'; + keyfile_port = atoi(end_bracket + 2); + } + if (host_matched && keyfile_port == pvar->ssh_state.tcpport) { + matched = 0; + // \x90ڑ\xB1\x83o\x81[\x83W\x83\x87\x83\x93\x83`\x83F\x83b\x83N\x82̂\xBD\x82߂\xC9 host_index \x82\xF0\x90i\x82߂Ă\xA9\x82甲\x82\xAF\x82\xE9 + host_index--; + do { + host_index++; + host_index += eat_to_end_of_pattern(data + host_index); + } while (data[host_index] == ','); + break; + } + } + else { + bracketed = data[host_index] == '['; + if (bracketed) { + end_bracket = strstr(data + host_index + 1, "]:"); + if (end_bracket != NULL) { + *end_bracket = ' '; + host_index++; + } + } + host_matched = match_pattern(data + host_index, pvar->ssh_state.hostname); + if (bracketed && end_bracket != NULL) { + *end_bracket = ']'; + keyfile_port = atoi(end_bracket + 2); + } + if (host_matched && keyfile_port == pvar->ssh_state.tcpport) { + matched = 1; + } + } + host_index += eat_to_end_of_pattern(data + host_index); + } while (data[host_index] == ','); + + // \x83z\x83X\x83g\x82\xAA\x93\x99\x82\xB5\x82\xAD\x82Ȃ\xA2 + if (!matched) { + do_write = 1; + } + // \x83z\x83X\x83g\x82\xAA\x93\x99\x82\xB5\x82\xA2 + else { + // \x88\xEA\x90؏\x91\x82\xAB\x8D\x9E\x82݂\xF0\x82\xB5\x82Ȃ\xA2\x81B + + } + } + + // \x8F\x91\x82\xAB\x8D\x9E\x82ݏ\x88\x97\x9D + if (do_write) { + length = pvar->hosts_state.file_data_index - data_index; + amount_written = + _write(fd, pvar->hosts_state.file_data + data_index, + length); + + if (amount_written != length) { + goto error1; + } + } + data_index = pvar->hosts_state.file_data_index; + } while (1); // \x8DŌ\xE3\x82܂œǂ\xDE + + error1: + close_result = _close(fd); + if (amount_written != length || close_result == -1) { + UTIL_get_lang_msg("MSG_HOSTS_WRITE_ERROR", pvar, + "An error occurred while trying to write the host key.\n" + "The host key could not be written."); + notify_nonfatal_error(pvar, pvar->ts->UIMsg); + goto error2; + } + + // \x8F\x91\x82\xAB\x8D\x9E\x82݈ꎞ\x83t\x83@\x83C\x83\x8B\x82\xA9\x82烊\x83l\x81[\x83\x80 + get_teraterm_dir_relative_name(buf, sizeof(buf), name); + _unlink(buf); + rename(filename, buf); + + error2: + _unlink(filename); + + finish_read_host_files(pvar, 0); + + // \x8DŌ\xE3\x82Ƀ\x81\x83\x82\x83\x8A\x82\xF0\x89\xF0\x95\xFA\x82\xB5\x82Ă\xA8\x82\xAD\x81B + key_freed = key_new(KEY_UNSPEC); + memcpy(key_freed, &key, sizeof(Key)); + key_free(key_freed); + } +} + + // // Unknown host\x82̃z\x83X\x83g\x8C\xF6\x8AJ\x8C\xAE\x82\xF0 known_hosts \x83t\x83@\x83C\x83\x8B\x82֕ۑ\xB6\x82\xB7\x82邩\x82ǂ\xA4\x82\xA9\x82\xF0 // \x83\x86\x81[\x83U\x82Ɋm\x94F\x82\xB3\x82\xB9\x82\xE9\x81B Modified: trunk/ttssh2/ttxssh/hosts.h =================================================================== --- trunk/ttssh2/ttxssh/hosts.h 2015-05-04 17:46:30 UTC (rev 5848) +++ trunk/ttssh2/ttxssh/hosts.h 2015-05-05 18:08:23 UTC (rev 5849) @@ -75,5 +75,7 @@ int HOSTS_compare_public_key(Key *src, Key *key); int HOSTS_hostkey_foreach(PTInstVar pvar, hostkeys_foreach_fn *callback, void *ctx); +void HOSTS_add_host_key(PTInstVar pvar, Key *key); +void HOSTS_delete_all_hostkeys(PTInstVar pvar); #endif Modified: trunk/ttssh2/ttxssh/key.c =================================================================== --- trunk/ttssh2/ttxssh/key.c 2015-05-04 17:46:30 UTC (rev 5848) +++ trunk/ttssh2/ttxssh/key.c 2015-05-05 18:08:23 UTC (rev 5849) @@ -1912,6 +1912,44 @@ return (ret); } +static void update_known_hosts(PTInstVar pvar, struct hostkeys_update_ctx *ctx) +{ + size_t i; + char msg[128]; + int dlgresult; + + // "/nosecuritywarning"\x82\xAA\x8Ew\x92肳\x82\xEA\x82Ă\xA2\x82\xE9\x8Fꍇ\x81A\x8DX\x90V\x82͈\xEA\x90؍s\x82\xED\x82Ȃ\xA2\x81B + if (pvar->nocheck_known_hosts) { + _snprintf_s(msg, sizeof(msg), _TRUNCATE, "Hostkey was not updated because `/nosecuritywarning' option was specified."); + notify_verbose_message(pvar, msg, LOG_LEVEL_VERBOSE); + goto error; + } + + // known_hosts\x83t\x83@\x83C\x83\x8B\x82̍X\x90V\x82\xF0\x8Ds\x82\xA4\x82\xBD\x82߁A\x83\x86\x81[\x83U\x82ɖ₢\x8D\x87\x82킹\x82\xF0\x8Ds\x82\xA4\x81B + if (pvar->settings.UpdateHostkeys == SSH_UPDATE_HOSTKEYS_ASK) { + _snprintf_s(msg, sizeof(msg), _TRUNCATE, "Accept updated hostkeys? (yes/no)"); + dlgresult = MessageBox(NULL, msg, "TTSSH: confirm", MB_YESNO | MB_ICONWARNING); + if (dlgresult != IDYES) { + _snprintf_s(msg, sizeof(msg), _TRUNCATE, "Hostkey was not updated because a user cancelled."); + notify_verbose_message(pvar, msg, LOG_LEVEL_VERBOSE); + goto error; + } + } + + // \x8CÂ\xA2\x83L\x81[\x82\xF0\x82\xB7\x82ׂč폜\x82\xB7\x82\xE9\x81B + HOSTS_delete_all_hostkeys(pvar); + + // \x90V\x82\xB5\x82\xA2\x83L\x81[\x82\xF0\x82\xB7\x82ׂēo\x98^\x82\xB7\x82\xE9\x81B + for (i = 0; i < ctx->nkeys; i++) { + HOSTS_add_host_key(pvar, ctx->keys[i]); + } + _snprintf_s(msg, sizeof(msg), _TRUNCATE, "Hostkey was successfully updated to known_hosts file."); + notify_verbose_message(pvar, msg, LOG_LEVEL_VERBOSE); + +error: + return; +} + // // SSH\x83T\x81[\x83o\x83z\x83X\x83g\x8C\xAE(known_hosts)\x82̎\xA9\x93\xAE\x8DX\x90V(OpenSSH 6.8 or later: host key rotation support) // @@ -1932,7 +1970,7 @@ Key *key = NULL, **tmp; // Tera Term\x82̐ݒ\xE8\x82ŁA\x93\x96\x8AY\x8B@\x94\\x82̃I\x83\x93\x83I\x83t\x82𐧌\xE4\x82ł\xAB\x82\xE9\x82悤\x82ɂ\xB7\x82\xE9\x81B - if (pvar->settings.UpdateHostkeys == 0) { + if (pvar->settings.UpdateHostkeys == SSH_UPDATE_HOSTKEYS_NO) { _snprintf_s(msg, sizeof(msg), _TRUNCATE, "Hostkey was not updated because ts.UpdateHostkeys is disabled."); notify_verbose_message(pvar, msg, LOG_LEVEL_VERBOSE); return 1; @@ -2008,32 +2046,33 @@ goto error; } - if ((ctx->keys_seen = calloc(ctx->nkeys, sizeof(*ctx->keys_seen))) == NULL) { + if ((ctx->keys_seen = calloc(ctx->nkeys, sizeof(*ctx->keys_seen))) == NULL) { _snprintf_s(msg, sizeof(msg), _TRUNCATE, "Not memory: calloc ctx->keys %d", ctx->nkeys); notify_verbose_message(pvar, msg, LOG_LEVEL_FATAL); goto error; - } + } HOSTS_hostkey_foreach(pvar, hostkeys_find, ctx); // \x83T\x81[\x83o\x82\xAA\x91\x97\x82\xC1\x82Ă\xAB\x82\xBD\x8C\xAE\x8C\xF3\x95\xE2\x8CQ\x82\xA9\x82\xE7\x81A\x82\xA2\x82\xAD\x82̌\xAE\x82\xF0\x90V\x8BK\x92lj\xC1\x82\xB7\x82\xE9\x82̂\xA9\x82𐔂\xA6\x82\xE9\x81B - ctx->nnew = 0; - for (i = 0; i < ctx->nkeys; i++) { - if (!ctx->keys_seen[i]) - ctx->nnew++; + ctx->nnew = 0; + for (i = 0; i < ctx->nkeys; i++) { + if (!ctx->keys_seen[i]) + ctx->nnew++; } - _snprintf_s(msg, sizeof(msg), _TRUNCATE, "%u keys from server: %u new, %u retained. %u to remove", - ctx->nkeys, ctx->nnew, ctx->nkeys - ctx->nnew, ctx->nold); + _snprintf_s(msg, sizeof(msg), _TRUNCATE, "%u keys from server: %u new, %u retained. %u to remove", + ctx->nkeys, ctx->nnew, ctx->nkeys - ctx->nnew, ctx->nold); notify_verbose_message(pvar, msg, LOG_LEVEL_VERBOSE); // \x90V\x8BK\x92lj\xC1\x82\xB7\x82錮\x82̓[\x83\x8D\x82\xBE\x82\xAA\x81Adeprecated\x82Ȍ\xAE\x82\xAA\x91\xB6\x8D݂\xB7\x82\xE9\x81B if (ctx->nnew == 0 && ctx->nold != 0) { - // TODO: + update_known_hosts(pvar, ctx); } else if (ctx->nnew != 0) { // \x90V\x8BK\x92lj\xC1\x82\xB7\x82\xE9\x82ׂ\xAB\x8C\xAE\x82\xAA\x91\xB6\x8D݂\xB7\x82\xE9\x81B // TODO: + update_known_hosts(pvar, ctx); } Modified: trunk/ttssh2/ttxssh/ttxssh.c =================================================================== --- trunk/ttssh2/ttxssh/ttxssh.c 2015-05-04 17:46:30 UTC (rev 5848) +++ trunk/ttssh2/ttxssh/ttxssh.c 2015-05-05 18:08:23 UTC (rev 5849) @@ -475,7 +475,7 @@ READ_STD_STRING_OPTION(X11Display); - settings->UpdateHostkeys = read_BOOL_option(fileName, "UpdateHostkeys", FALSE); + settings->UpdateHostkeys = GetPrivateProfileInt("TTSSH", "UpdateHostkeys", 0, fileName); clear_local_settings(pvar); } @@ -583,8 +583,9 @@ WritePrivateProfileString("TTSSH", "X11Display", settings->X11Display, fileName); - WritePrivateProfileString("TTSSH", "UpdateHostkeys", - settings->UpdateHostkeys ? "1" : "0", fileName); + _snprintf_s(buf, sizeof(buf), _TRUNCATE, + "%d", settings->UpdateHostkeys); + WritePrivateProfileString("TTSSH", "UpdateHostkeys", buf, fileName); } Modified: trunk/ttssh2/ttxssh/ttxssh.h =================================================================== --- trunk/ttssh2/ttxssh/ttxssh.h 2015-05-04 17:46:30 UTC (rev 5848) +++ trunk/ttssh2/ttxssh/ttxssh.h 2015-05-05 18:08:23 UTC (rev 5849) @@ -102,7 +102,16 @@ #define POPUP_MSG_default 0 #define POPUP_MSG_FWD_received_data (1 << 0) + /* + * Host key rotation + */ +#define SSH_UPDATE_HOSTKEYS_NO 0 +#define SSH_UPDATE_HOSTKEYS_YES 1 +#define SSH_UPDATE_HOSTKEYS_ASK 2 + + +/* These are the fields that WOULD go in Tera Term's 'ts' structure, if we could put them there. */