Mercurial > dropbear
comparison cli-kex.c @ 51:095d689fed16
- Hostkey checking is mostly there, just aren't appending yet.
- Rearranged various bits of the fingerprint/base64 type code, so it
can be shared between versions
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Sun, 08 Aug 2004 16:17:05 +0000 |
parents | e2a1eaa19f22 |
children | bdc97a5719f4 |
comparison
equal
deleted
inserted
replaced
49:cc59bfcdee17 | 51:095d689fed16 |
---|---|
35 #include "random.h" | 35 #include "random.h" |
36 #include "runopts.h" | 36 #include "runopts.h" |
37 #include "signkey.h" | 37 #include "signkey.h" |
38 | 38 |
39 | 39 |
40 static void checkhostkey(unsigned char* keyblob, unsigned int keybloblen); | |
41 #define MAX_KNOWNHOSTS_LINE 4500 | |
40 | 42 |
41 void send_msg_kexdh_init() { | 43 void send_msg_kexdh_init() { |
42 | 44 |
43 cli_ses.dh_e = (mp_int*)m_malloc(sizeof(mp_int)); | 45 cli_ses.dh_e = (mp_int*)m_malloc(sizeof(mp_int)); |
44 cli_ses.dh_x = (mp_int*)m_malloc(sizeof(mp_int)); | 46 cli_ses.dh_x = (mp_int*)m_malloc(sizeof(mp_int)); |
56 /* Handle a diffie-hellman key exchange reply. */ | 58 /* Handle a diffie-hellman key exchange reply. */ |
57 void recv_msg_kexdh_reply() { | 59 void recv_msg_kexdh_reply() { |
58 | 60 |
59 mp_int dh_f; | 61 mp_int dh_f; |
60 sign_key *hostkey = NULL; | 62 sign_key *hostkey = NULL; |
61 int type, keylen; | 63 unsigned int type, keybloblen; |
64 unsigned char* keyblob = NULL; | |
65 | |
62 | 66 |
63 TRACE(("enter recv_msg_kexdh_reply")); | 67 TRACE(("enter recv_msg_kexdh_reply")); |
64 type = ses.newkeys->algo_hostkey; | 68 type = ses.newkeys->algo_hostkey; |
65 TRACE(("type is %d", type)); | 69 TRACE(("type is %d", type)); |
66 | 70 |
67 hostkey = new_sign_key(); | 71 hostkey = new_sign_key(); |
68 keylen = buf_getint(ses.payload); | 72 keybloblen = buf_getint(ses.payload); |
73 | |
74 keyblob = buf_getptr(ses.payload, keybloblen); | |
75 if (!ses.kexstate.donefirstkex) { | |
76 /* Only makes sense the first time */ | |
77 checkhostkey(keyblob, keybloblen); | |
78 } | |
69 | 79 |
70 if (buf_get_pub_key(ses.payload, hostkey, &type) != DROPBEAR_SUCCESS) { | 80 if (buf_get_pub_key(ses.payload, hostkey, &type) != DROPBEAR_SUCCESS) { |
71 TRACE(("failed getting pubkey")); | 81 TRACE(("failed getting pubkey")); |
72 dropbear_exit("Bad KEX packet"); | 82 dropbear_exit("Bad KEX packet"); |
73 } | 83 } |
84 if (buf_verify(ses.payload, hostkey, ses.hash, SHA1_HASH_SIZE) | 94 if (buf_verify(ses.payload, hostkey, ses.hash, SHA1_HASH_SIZE) |
85 != DROPBEAR_SUCCESS) { | 95 != DROPBEAR_SUCCESS) { |
86 dropbear_exit("Bad hostkey signature"); | 96 dropbear_exit("Bad hostkey signature"); |
87 } | 97 } |
88 | 98 |
89 /* XXX TODO */ | |
90 dropbear_log(LOG_WARNING,"Not checking hostkey fingerprint for the moment"); | |
91 | |
92 sign_key_free(hostkey); | 99 sign_key_free(hostkey); |
93 hostkey = NULL; | 100 hostkey = NULL; |
94 | 101 |
95 send_msg_newkeys(); | 102 send_msg_newkeys(); |
96 ses.requirenext = SSH_MSG_NEWKEYS; | 103 ses.requirenext = SSH_MSG_NEWKEYS; |
97 TRACE(("leave recv_msg_kexdh_init")); | 104 TRACE(("leave recv_msg_kexdh_init")); |
98 } | 105 } |
106 | |
107 static void ask_to_confirm(unsigned char* keyblob, unsigned int keybloblen) { | |
108 | |
109 char* fp = NULL; | |
110 | |
111 fp = sign_key_fingerprint(keyblob, keybloblen); | |
112 fprintf(stderr, "\nHost '%s' is not in the trusted hosts file.\n(fingerprint %s)\nDo you want to continue connecting? (y/n)\n", | |
113 cli_opts.remotehost, | |
114 fp); | |
115 | |
116 if (getc(stdin) == 'y') { | |
117 m_free(fp); | |
118 return; | |
119 } | |
120 | |
121 dropbear_exit("Didn't validate host key"); | |
122 } | |
123 | |
124 static void checkhostkey(unsigned char* keyblob, unsigned int keybloblen) { | |
125 | |
126 char * filename = NULL; | |
127 FILE *hostsfile = NULL; | |
128 struct passwd *pw = NULL; | |
129 unsigned int len, hostlen; | |
130 const char *algoname = NULL; | |
131 buffer * line = NULL; | |
132 int ret; | |
133 | |
134 pw = getpwuid(getuid()); | |
135 | |
136 if (pw == NULL) { | |
137 dropbear_exit("Failed to get homedir"); | |
138 } | |
139 | |
140 len = strlen(pw->pw_dir); | |
141 filename = m_malloc(len + 18); /* "/.ssh/known_hosts" and null-terminator*/ | |
142 | |
143 snprintf(filename, len+18, "%s/.ssh", pw->pw_dir); | |
144 /* Check that ~/.ssh exists - easiest way is just to mkdir */ | |
145 if (mkdir(filename, S_IRWXU) != 0) { | |
146 if (errno != EEXIST) { | |
147 ask_to_confirm(keyblob, keybloblen); | |
148 goto out; /* only get here on success */ | |
149 } | |
150 } | |
151 | |
152 snprintf(filename, len+18, "%s/.ssh/known_hosts", pw->pw_dir); | |
153 hostsfile = fopen(filename, "r+"); | |
154 if (hostsfile == NULL) { | |
155 ask_to_confirm(keyblob, keybloblen); | |
156 goto out; /* We only get here on success */ | |
157 } | |
158 | |
159 line = buf_new(MAX_KNOWNHOSTS_LINE); | |
160 hostlen = strlen(cli_opts.remotehost); | |
161 | |
162 do { | |
163 if (buf_getline(line, hostsfile) == DROPBEAR_FAILURE) { | |
164 TRACE(("failed reading line: prob EOF")); | |
165 break; | |
166 } | |
167 | |
168 /* The line is too short to be sensible */ | |
169 /* "30" is 'enough to hold ssh-dss plus the spaces, ie so we don't | |
170 * buf_getfoo() past the end and die horribly - the base64 parsing | |
171 * code is what tiptoes up to the end nicely */ | |
172 if (line->len < (hostlen+30) ) { | |
173 TRACE(("line is too short to be sensible")); | |
174 continue; | |
175 } | |
176 | |
177 /* Compare hostnames */ | |
178 if (strncmp(cli_opts.remotehost, buf_getptr(line, hostlen), | |
179 hostlen) != 0) { | |
180 TRACE(("hosts don't match")); | |
181 continue; | |
182 } | |
183 | |
184 buf_incrpos(line, hostlen); | |
185 if (buf_getbyte(line) != ' ') { | |
186 /* there wasn't a space after the hostname, something dodgy */ | |
187 TRACE(("missing space afte matching hostname")); | |
188 continue; | |
189 } | |
190 | |
191 algoname = signkey_name_from_type(ses.newkeys->algo_hostkey, &len); | |
192 if ( strncmp(buf_getptr(line, len), algoname, len) != 0) { | |
193 TRACE(("algo doesn't match")); | |
194 continue; | |
195 } | |
196 | |
197 buf_incrpos(line, len); | |
198 if (buf_getbyte(line) != ' ') { | |
199 TRACE(("missing space after algo")); | |
200 continue; | |
201 } | |
202 | |
203 /* Now we're at the interesting hostkey */ | |
204 ret = cmp_base64_key(keyblob, keybloblen, algoname, len, line); | |
205 | |
206 if (ret == DROPBEAR_SUCCESS) { | |
207 /* Good matching key */ | |
208 TRACE(("good matching key")); | |
209 goto out; | |
210 } | |
211 | |
212 /* The keys didn't match. eep. */ | |
213 } while (1); /* keep going 'til something happens */ | |
214 | |
215 /* Key doesn't exist yet */ | |
216 ask_to_confirm(keyblob, keybloblen); | |
217 /* If we get here, they said yes */ | |
218 | |
219 out: | |
220 if (hostsfile != NULL) { | |
221 fclose(hostsfile); | |
222 } | |
223 m_free(filename); | |
224 buf_free(line); | |
225 } |