1709795f |
1 | /* |
2 | * uxstore.c: Unix-specific implementation of the interface defined |
3 | * in storage.h. |
4 | */ |
5 | |
6 | #include <stdio.h> |
7 | #include <stdlib.h> |
0ac15bdc |
8 | #include <ctype.h> |
c5e438ec |
9 | #include <unistd.h> |
10 | #include <fcntl.h> |
11 | #include <sys/stat.h> |
12 | #include <sys/types.h> |
1709795f |
13 | #include "putty.h" |
14 | #include "storage.h" |
0ac15bdc |
15 | #include "tree234.h" |
1709795f |
16 | |
faec60ed |
17 | /* |
18 | * For the moment, the only existing Unix utility is pterm and that |
19 | * has no GUI configuration at all, so our write routines need do |
20 | * nothing. Eventually I suppose these will read and write an rc |
21 | * file somewhere or other. |
22 | */ |
1709795f |
23 | |
c85623f9 |
24 | void *open_settings_w(const char *sessionname) |
1709795f |
25 | { |
26 | return NULL; |
27 | } |
28 | |
c85623f9 |
29 | void write_setting_s(void *handle, const char *key, const char *value) |
1709795f |
30 | { |
31 | } |
32 | |
c85623f9 |
33 | void write_setting_i(void *handle, const char *key, int value) |
1709795f |
34 | { |
35 | } |
36 | |
37 | void close_settings_w(void *handle) |
38 | { |
39 | } |
40 | |
faec60ed |
41 | /* |
42 | * Reading settings, for the moment, is done by retrieving X |
43 | * resources from the X display. When we introduce disk files, I |
44 | * think what will happen is that the X resources will override |
45 | * PuTTY's inbuilt defaults, but that the disk files will then |
46 | * override those. This isn't optimal, but it's the best I can |
47 | * immediately work out. |
48 | */ |
49 | |
0ac15bdc |
50 | struct xrm_string { |
c85623f9 |
51 | const char *key; |
52 | const char *value; |
0ac15bdc |
53 | }; |
54 | |
55 | static tree234 *xrmtree = NULL; |
56 | |
57 | int xrmcmp(void *av, void *bv) |
58 | { |
59 | struct xrm_string *a = (struct xrm_string *)av; |
60 | struct xrm_string *b = (struct xrm_string *)bv; |
61 | return strcmp(a->key, b->key); |
62 | } |
63 | |
64 | void provide_xrm_string(char *string) |
65 | { |
c85623f9 |
66 | char *p, *q, *key; |
0ac15bdc |
67 | struct xrm_string *xrms, *ret; |
68 | |
69 | p = q = strchr(string, ':'); |
70 | if (!q) { |
71 | fprintf(stderr, "pterm: expected a colon in resource string" |
72 | " \"%s\"\n", string); |
73 | return; |
74 | } |
75 | q++; |
76 | while (p > string && p[-1] != '.' && p[-1] != '*') |
77 | p--; |
78 | xrms = smalloc(sizeof(struct xrm_string)); |
c85623f9 |
79 | key = smalloc(q-p); |
80 | memcpy(key, p, q-p); |
81 | key[q-p-1] = '\0'; |
82 | xrms->key = key; |
e93ed432 |
83 | while (*q && isspace((unsigned char)*q)) |
0ac15bdc |
84 | q++; |
85 | xrms->value = dupstr(q); |
86 | |
87 | if (!xrmtree) |
88 | xrmtree = newtree234(xrmcmp); |
89 | |
90 | ret = add234(xrmtree, xrms); |
91 | if (ret) { |
92 | /* Override an existing string. */ |
93 | del234(xrmtree, ret); |
94 | add234(xrmtree, xrms); |
95 | } |
96 | } |
97 | |
c85623f9 |
98 | const char *get_setting(const char *key) |
0ac15bdc |
99 | { |
100 | struct xrm_string tmp, *ret; |
101 | tmp.key = key; |
102 | if (xrmtree) { |
103 | ret = find234(xrmtree, &tmp, NULL); |
104 | if (ret) |
105 | return ret->value; |
106 | } |
c5e438ec |
107 | return x_get_default(key); |
0ac15bdc |
108 | } |
109 | |
c85623f9 |
110 | void *open_settings_r(const char *sessionname) |
1709795f |
111 | { |
faec60ed |
112 | static int thing_to_return_an_arbitrary_non_null_pointer_to; |
c5e438ec |
113 | return &thing_to_return_an_arbitrary_non_null_pointer_to; |
1709795f |
114 | } |
115 | |
c85623f9 |
116 | char *read_setting_s(void *handle, const char *key, char *buffer, int buflen) |
1709795f |
117 | { |
c85623f9 |
118 | const char *val = get_setting(key); |
faec60ed |
119 | if (!val) |
120 | return NULL; |
121 | else { |
122 | strncpy(buffer, val, buflen); |
123 | buffer[buflen-1] = '\0'; |
124 | return buffer; |
125 | } |
1709795f |
126 | } |
127 | |
c85623f9 |
128 | int read_setting_i(void *handle, const char *key, int defvalue) |
1709795f |
129 | { |
c85623f9 |
130 | const char *val = get_setting(key); |
faec60ed |
131 | if (!val) |
132 | return defvalue; |
133 | else |
134 | return atoi(val); |
1709795f |
135 | } |
136 | |
9a30e26b |
137 | int read_setting_fontspec(void *handle, const char *name, FontSpec *result) |
138 | { |
139 | return !!read_setting_s(handle, name, result->name, sizeof(result->name)); |
140 | } |
141 | int read_setting_filename(void *handle, const char *name, Filename *result) |
142 | { |
143 | return !!read_setting_s(handle, name, result->path, sizeof(result->path)); |
144 | } |
145 | |
146 | void write_setting_fontspec(void *handle, const char *name, FontSpec result) |
147 | { |
148 | write_setting_s(handle, name, result.name); |
149 | } |
150 | void write_setting_filename(void *handle, const char *name, Filename result) |
151 | { |
152 | write_setting_s(handle, name, result.path); |
153 | } |
154 | |
1709795f |
155 | void close_settings_r(void *handle) |
156 | { |
157 | } |
158 | |
c85623f9 |
159 | void del_settings(const char *sessionname) |
1709795f |
160 | { |
161 | } |
162 | |
163 | void *enum_settings_start(void) |
164 | { |
165 | return NULL; |
166 | } |
167 | |
168 | char *enum_settings_next(void *handle, char *buffer, int buflen) |
169 | { |
170 | return NULL; |
171 | } |
172 | |
173 | void enum_settings_finish(void *handle) |
174 | { |
175 | } |
176 | |
c5e438ec |
177 | enum { |
d9c40fd6 |
178 | INDEX_DIR, INDEX_HOSTKEYS, INDEX_RANDSEED |
c5e438ec |
179 | }; |
180 | |
181 | static void make_filename(char *filename, int index) |
182 | { |
183 | char *home; |
184 | int len; |
185 | home = getenv("HOME"); |
186 | strncpy(filename, home, FILENAME_MAX); |
187 | len = strlen(filename); |
188 | strncpy(filename + len, |
189 | index == INDEX_DIR ? "/.putty" : |
190 | index == INDEX_HOSTKEYS ? "/.putty/sshhostkeys" : |
d9c40fd6 |
191 | index == INDEX_RANDSEED ? "/.putty/randomseed" : |
c5e438ec |
192 | "/.putty/ERROR", FILENAME_MAX - len); |
193 | filename[FILENAME_MAX-1] = '\0'; |
194 | } |
195 | |
196 | /* |
197 | * Read an entire line of text from a file. Return a buffer |
198 | * malloced to be as big as necessary (caller must free). |
199 | */ |
200 | static char *fgetline(FILE *fp) |
201 | { |
202 | char *ret = smalloc(512); |
203 | int size = 512, len = 0; |
204 | while (fgets(ret + len, size - len, fp)) { |
205 | len += strlen(ret + len); |
206 | if (ret[len-1] == '\n') |
207 | break; /* got a newline, we're done */ |
208 | size = len + 512; |
209 | ret = srealloc(ret, size); |
210 | } |
211 | if (len == 0) { /* first fgets returned NULL */ |
212 | sfree(ret); |
213 | return NULL; |
214 | } |
215 | ret[len] = '\0'; |
216 | return ret; |
217 | } |
218 | |
219 | /* |
220 | * Lines in the host keys file are of the form |
221 | * |
222 | * type@port:hostname keydata |
223 | * |
224 | * e.g. |
225 | * |
226 | * rsa@22:foovax.example.org 0x23,0x293487364395345345....2343 |
227 | */ |
c85623f9 |
228 | int verify_host_key(const char *hostname, int port, |
229 | const char *keytype, const char *key) |
1709795f |
230 | { |
c5e438ec |
231 | FILE *fp; |
232 | char filename[FILENAME_MAX]; |
233 | char *line; |
234 | int ret; |
235 | |
236 | make_filename(filename, INDEX_HOSTKEYS); |
237 | fp = fopen(filename, "r"); |
238 | if (!fp) |
239 | return 1; /* key does not exist */ |
240 | |
241 | ret = 1; |
242 | while ( (line = fgetline(fp)) ) { |
243 | int i; |
244 | char *p = line; |
245 | char porttext[20]; |
246 | |
247 | line[strcspn(line, "\n")] = '\0'; /* strip trailing newline */ |
248 | |
249 | i = strlen(keytype); |
250 | if (strncmp(p, keytype, i)) |
251 | goto done; |
252 | p += i; |
253 | |
254 | if (*p != '@') |
255 | goto done; |
256 | p++; |
257 | |
258 | sprintf(porttext, "%d", port); |
259 | i = strlen(porttext); |
260 | if (strncmp(p, porttext, i)) |
261 | goto done; |
262 | p += i; |
263 | |
264 | if (*p != ':') |
265 | goto done; |
266 | p++; |
267 | |
268 | i = strlen(hostname); |
269 | if (strncmp(p, hostname, i)) |
270 | goto done; |
271 | p += i; |
272 | |
273 | if (*p != ' ') |
274 | goto done; |
275 | p++; |
276 | |
277 | /* |
278 | * Found the key. Now just work out whether it's the right |
279 | * one or not. |
280 | */ |
281 | if (!strcmp(p, key)) |
282 | ret = 0; /* key matched OK */ |
283 | else |
284 | ret = 2; /* key mismatch */ |
285 | |
286 | done: |
287 | sfree(line); |
288 | if (ret != 1) |
289 | break; |
290 | } |
291 | |
292 | return ret; |
1709795f |
293 | } |
294 | |
c85623f9 |
295 | void store_host_key(const char *hostname, int port, |
296 | const char *keytype, const char *key) |
1709795f |
297 | { |
c5e438ec |
298 | FILE *fp; |
299 | int fd; |
300 | char filename[FILENAME_MAX]; |
301 | |
302 | make_filename(filename, INDEX_HOSTKEYS); |
303 | fd = open(filename, O_CREAT | O_APPEND | O_RDWR, 0600); |
304 | if (fd < 0) { |
305 | char dir[FILENAME_MAX]; |
306 | |
307 | make_filename(dir, INDEX_DIR); |
308 | mkdir(dir, 0700); |
309 | fd = open(filename, O_CREAT | O_APPEND | O_RDWR, 0600); |
310 | } |
311 | if (fd < 0) { |
312 | perror(filename); |
313 | exit(1); |
314 | } |
315 | fp = fdopen(fd, "a"); |
316 | fprintf(fp, "%s@%d:%s %s\n", keytype, port, hostname, key); |
317 | fclose(fp); |
1709795f |
318 | } |
319 | |
320 | void read_random_seed(noise_consumer_t consumer) |
321 | { |
d9c40fd6 |
322 | int fd; |
323 | char fname[FILENAME_MAX]; |
324 | |
325 | make_filename(fname, INDEX_RANDSEED); |
326 | fd = open(fname, O_RDONLY); |
327 | if (fd) { |
328 | char buf[512]; |
329 | int ret; |
330 | while ( (ret = read(fd, buf, sizeof(buf))) > 0) |
331 | consumer(buf, ret); |
332 | close(fd); |
333 | } |
1709795f |
334 | } |
335 | |
336 | void write_random_seed(void *data, int len) |
337 | { |
d9c40fd6 |
338 | int fd; |
339 | char fname[FILENAME_MAX]; |
340 | |
341 | make_filename(fname, INDEX_RANDSEED); |
e3ac3c05 |
342 | /* |
343 | * Don't truncate the random seed file if it already exists; if |
344 | * something goes wrong half way through writing it, it would |
345 | * be better to leave the old data there than to leave it empty. |
346 | */ |
347 | fd = open(fname, O_CREAT | O_WRONLY, 0600); |
d9c40fd6 |
348 | if (fd < 0) { |
349 | char dir[FILENAME_MAX]; |
350 | |
351 | make_filename(dir, INDEX_DIR); |
352 | mkdir(dir, 0700); |
e3ac3c05 |
353 | fd = open(fname, O_CREAT | O_WRONLY, 0600); |
d9c40fd6 |
354 | } |
355 | |
356 | while (len > 0) { |
357 | int ret = write(fd, data, len); |
358 | if (ret <= 0) break; |
359 | len -= ret; |
360 | data = (char *)data + len; |
361 | } |
362 | |
363 | close(fd); |
1709795f |
364 | } |
365 | |
366 | void cleanup_all(void) |
367 | { |
368 | } |