Having created and used uxsel, it actually turns out to be
[u/mdw/putty] / unix / uxputty.c
1 /*
2 * Unix PuTTY main program.
3 */
4
5 #include <stdio.h>
6 #include <assert.h>
7 #include <termios.h>
8 #include <unistd.h>
9
10 #include "putty.h"
11 #include "storage.h"
12
13 /*
14 * FIXME: At least some of these functions should be replaced with
15 * GTK GUI error-box-type things.
16 *
17 * In particular, all the termios-type things must go, and
18 * termios.h should disappear from the above #include list.
19 */
20 void fatalbox(char *p, ...)
21 {
22 va_list ap;
23 fprintf(stderr, "FATAL ERROR: ");
24 va_start(ap, p);
25 vfprintf(stderr, p, ap);
26 va_end(ap);
27 fputc('\n', stderr);
28 cleanup_exit(1);
29 }
30 void connection_fatal(void *frontend, char *p, ...)
31 {
32 va_list ap;
33 fprintf(stderr, "FATAL ERROR: ");
34 va_start(ap, p);
35 vfprintf(stderr, p, ap);
36 va_end(ap);
37 fputc('\n', stderr);
38 cleanup_exit(1);
39 }
40 void cmdline_error(char *p, ...)
41 {
42 va_list ap;
43 fprintf(stderr, "plink: ");
44 va_start(ap, p);
45 vfprintf(stderr, p, ap);
46 va_end(ap);
47 fputc('\n', stderr);
48 exit(1);
49 }
50
51 /*
52 * Clean up and exit.
53 */
54 void cleanup_exit(int code)
55 {
56 /*
57 * Clean up.
58 */
59 sk_cleanup();
60 random_save_seed();
61 exit(code);
62 }
63
64 void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
65 char *keystr, char *fingerprint)
66 {
67 int ret;
68
69 static const char absentmsg[] =
70 "The server's host key is not cached. You have no guarantee\n"
71 "that the server is the computer you think it is.\n"
72 "The server's key fingerprint is:\n"
73 "%s\n"
74 "If you trust this host, enter \"y\" to add the key to\n"
75 "PuTTY's cache and carry on connecting.\n"
76 "If you want to carry on connecting just once, without\n"
77 "adding the key to the cache, enter \"n\".\n"
78 "If you do not trust this host, press Return to abandon the\n"
79 "connection.\n"
80 "Store key in cache? (y/n) ";
81
82 static const char wrongmsg[] =
83 "WARNING - POTENTIAL SECURITY BREACH!\n"
84 "The server's host key does not match the one PuTTY has\n"
85 "cached. This means that either the server administrator\n"
86 "has changed the host key, or you have actually connected\n"
87 "to another computer pretending to be the server.\n"
88 "The new key fingerprint is:\n"
89 "%s\n"
90 "If you were expecting this change and trust the new key,\n"
91 "enter \"y\" to update PuTTY's cache and continue connecting.\n"
92 "If you want to carry on connecting but without updating\n"
93 "the cache, enter \"n\".\n"
94 "If you want to abandon the connection completely, press\n"
95 "Return to cancel. Pressing Return is the ONLY guaranteed\n"
96 "safe choice.\n"
97 "Update cached key? (y/n, Return cancels connection) ";
98
99 static const char abandoned[] = "Connection abandoned.\n";
100
101 char line[32];
102
103 /*
104 * Verify the key.
105 */
106 ret = verify_host_key(host, port, keytype, keystr);
107
108 if (ret == 0) /* success - key matched OK */
109 return;
110
111 if (ret == 2) { /* key was different */
112 fprintf(stderr, wrongmsg, fingerprint);
113 fflush(stderr);
114 }
115 if (ret == 1) { /* key was absent */
116 fprintf(stderr, absentmsg, fingerprint);
117 fflush(stderr);
118 }
119
120 {
121 struct termios oldmode, newmode;
122 tcgetattr(0, &oldmode);
123 newmode = oldmode;
124 newmode.c_lflag |= ECHO | ISIG | ICANON;
125 tcsetattr(0, TCSANOW, &newmode);
126 line[0] = '\0';
127 read(0, line, sizeof(line) - 1);
128 tcsetattr(0, TCSANOW, &oldmode);
129 }
130
131 if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n') {
132 if (line[0] == 'y' || line[0] == 'Y')
133 store_host_key(host, port, keytype, keystr);
134 } else {
135 fprintf(stderr, abandoned);
136 cleanup_exit(0);
137 }
138 }
139
140 /*
141 * Ask whether the selected cipher is acceptable (since it was
142 * below the configured 'warn' threshold).
143 * cs: 0 = both ways, 1 = client->server, 2 = server->client
144 */
145 void askcipher(void *frontend, char *ciphername, int cs)
146 {
147 static const char msg[] =
148 "The first %scipher supported by the server is\n"
149 "%s, which is below the configured warning threshold.\n"
150 "Continue with connection? (y/n) ";
151 static const char abandoned[] = "Connection abandoned.\n";
152
153 char line[32];
154
155 fprintf(stderr, msg,
156 (cs == 0) ? "" :
157 (cs == 1) ? "client-to-server " : "server-to-client ",
158 ciphername);
159 fflush(stderr);
160
161 {
162 struct termios oldmode, newmode;
163 tcgetattr(0, &oldmode);
164 newmode = oldmode;
165 newmode.c_lflag |= ECHO | ISIG | ICANON;
166 tcsetattr(0, TCSANOW, &newmode);
167 line[0] = '\0';
168 read(0, line, sizeof(line) - 1);
169 tcsetattr(0, TCSANOW, &oldmode);
170 }
171
172 if (line[0] == 'y' || line[0] == 'Y') {
173 return;
174 } else {
175 fprintf(stderr, abandoned);
176 cleanup_exit(0);
177 }
178 }
179
180 void old_keyfile_warning(void)
181 {
182 static const char message[] =
183 "You are loading an SSH 2 private key which has an\n"
184 "old version of the file format. This means your key\n"
185 "file is not fully tamperproof. Future versions of\n"
186 "PuTTY may stop supporting this private key format,\n"
187 "so we recommend you convert your key to the new\n"
188 "format.\n"
189 "\n"
190 "Once the key is loaded into PuTTYgen, you can perform\n"
191 "this conversion simply by saving it again.\n";
192
193 fputs(message, stderr);
194 }
195
196 /*
197 * Another bunch of temporary stub functions. These ones will want
198 * removing by means of implementing them properly: libcharset
199 * should invent its own sensible format for codepage names and a
200 * means of enumerating them, and printer_enum needs to be dealt
201 * with somehow or other too.
202 */
203
204 char *cp_name(int codepage)
205 {
206 return "";
207 }
208 char *cp_enumerate(int index)
209 {
210 return NULL;
211 }
212 int decode_codepage(char *cp_name)
213 {
214 return -2;
215 }
216
217 printer_enum *printer_start_enum(int *nprinters_ptr) {
218 *nprinters_ptr = 0;
219 return NULL;
220 }
221 char *printer_get_name(printer_enum *pe, int i) { return NULL;
222 }
223 void printer_finish_enum(printer_enum *pe) { }
224
225 Backend *select_backend(Config *cfg)
226 {
227 int i;
228 Backend *back = NULL;
229 for (i = 0; backends[i].backend != NULL; i++)
230 if (backends[i].protocol == cfg->protocol) {
231 back = backends[i].backend;
232 break;
233 }
234 assert(back != NULL);
235 return back;
236 }
237
238 int cfgbox(Config *cfg)
239 {
240 extern int do_config_box(const char *title, Config *cfg);
241 return do_config_box("PuTTY Configuration", cfg);
242 }
243
244 int main(int argc, char **argv)
245 {
246 extern int pt_main(int argc, char **argv);
247 sk_init();
248 flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
249 default_protocol = be_default_protocol;
250 /* Find the appropriate default port. */
251 {
252 int i;
253 default_port = 0; /* illegal */
254 for (i = 0; backends[i].backend != NULL; i++)
255 if (backends[i].protocol == default_protocol) {
256 default_port = backends[i].backend->default_port;
257 break;
258 }
259 }
260 return pt_main(argc, argv);
261 }