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; |
0ac15bdc |
83 | while (*q && isspace(*q)) |
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 | |
137 | void close_settings_r(void *handle) |
138 | { |
139 | } |
140 | |
c85623f9 |
141 | void del_settings(const char *sessionname) |
1709795f |
142 | { |
143 | } |
144 | |
145 | void *enum_settings_start(void) |
146 | { |
147 | return NULL; |
148 | } |
149 | |
150 | char *enum_settings_next(void *handle, char *buffer, int buflen) |
151 | { |
152 | return NULL; |
153 | } |
154 | |
155 | void enum_settings_finish(void *handle) |
156 | { |
157 | } |
158 | |
c5e438ec |
159 | enum { |
d9c40fd6 |
160 | INDEX_DIR, INDEX_HOSTKEYS, INDEX_RANDSEED |
c5e438ec |
161 | }; |
162 | |
163 | static void make_filename(char *filename, int index) |
164 | { |
165 | char *home; |
166 | int len; |
167 | home = getenv("HOME"); |
168 | strncpy(filename, home, FILENAME_MAX); |
169 | len = strlen(filename); |
170 | strncpy(filename + len, |
171 | index == INDEX_DIR ? "/.putty" : |
172 | index == INDEX_HOSTKEYS ? "/.putty/sshhostkeys" : |
d9c40fd6 |
173 | index == INDEX_RANDSEED ? "/.putty/randomseed" : |
c5e438ec |
174 | "/.putty/ERROR", FILENAME_MAX - len); |
175 | filename[FILENAME_MAX-1] = '\0'; |
176 | } |
177 | |
178 | /* |
179 | * Read an entire line of text from a file. Return a buffer |
180 | * malloced to be as big as necessary (caller must free). |
181 | */ |
182 | static char *fgetline(FILE *fp) |
183 | { |
184 | char *ret = smalloc(512); |
185 | int size = 512, len = 0; |
186 | while (fgets(ret + len, size - len, fp)) { |
187 | len += strlen(ret + len); |
188 | if (ret[len-1] == '\n') |
189 | break; /* got a newline, we're done */ |
190 | size = len + 512; |
191 | ret = srealloc(ret, size); |
192 | } |
193 | if (len == 0) { /* first fgets returned NULL */ |
194 | sfree(ret); |
195 | return NULL; |
196 | } |
197 | ret[len] = '\0'; |
198 | return ret; |
199 | } |
200 | |
201 | /* |
202 | * Lines in the host keys file are of the form |
203 | * |
204 | * type@port:hostname keydata |
205 | * |
206 | * e.g. |
207 | * |
208 | * rsa@22:foovax.example.org 0x23,0x293487364395345345....2343 |
209 | */ |
c85623f9 |
210 | int verify_host_key(const char *hostname, int port, |
211 | const char *keytype, const char *key) |
1709795f |
212 | { |
c5e438ec |
213 | FILE *fp; |
214 | char filename[FILENAME_MAX]; |
215 | char *line; |
216 | int ret; |
217 | |
218 | make_filename(filename, INDEX_HOSTKEYS); |
219 | fp = fopen(filename, "r"); |
220 | if (!fp) |
221 | return 1; /* key does not exist */ |
222 | |
223 | ret = 1; |
224 | while ( (line = fgetline(fp)) ) { |
225 | int i; |
226 | char *p = line; |
227 | char porttext[20]; |
228 | |
229 | line[strcspn(line, "\n")] = '\0'; /* strip trailing newline */ |
230 | |
231 | i = strlen(keytype); |
232 | if (strncmp(p, keytype, i)) |
233 | goto done; |
234 | p += i; |
235 | |
236 | if (*p != '@') |
237 | goto done; |
238 | p++; |
239 | |
240 | sprintf(porttext, "%d", port); |
241 | i = strlen(porttext); |
242 | if (strncmp(p, porttext, i)) |
243 | goto done; |
244 | p += i; |
245 | |
246 | if (*p != ':') |
247 | goto done; |
248 | p++; |
249 | |
250 | i = strlen(hostname); |
251 | if (strncmp(p, hostname, i)) |
252 | goto done; |
253 | p += i; |
254 | |
255 | if (*p != ' ') |
256 | goto done; |
257 | p++; |
258 | |
259 | /* |
260 | * Found the key. Now just work out whether it's the right |
261 | * one or not. |
262 | */ |
263 | if (!strcmp(p, key)) |
264 | ret = 0; /* key matched OK */ |
265 | else |
266 | ret = 2; /* key mismatch */ |
267 | |
268 | done: |
269 | sfree(line); |
270 | if (ret != 1) |
271 | break; |
272 | } |
273 | |
274 | return ret; |
1709795f |
275 | } |
276 | |
c85623f9 |
277 | void store_host_key(const char *hostname, int port, |
278 | const char *keytype, const char *key) |
1709795f |
279 | { |
c5e438ec |
280 | FILE *fp; |
281 | int fd; |
282 | char filename[FILENAME_MAX]; |
283 | |
284 | make_filename(filename, INDEX_HOSTKEYS); |
285 | fd = open(filename, O_CREAT | O_APPEND | O_RDWR, 0600); |
286 | if (fd < 0) { |
287 | char dir[FILENAME_MAX]; |
288 | |
289 | make_filename(dir, INDEX_DIR); |
290 | mkdir(dir, 0700); |
291 | fd = open(filename, O_CREAT | O_APPEND | O_RDWR, 0600); |
292 | } |
293 | if (fd < 0) { |
294 | perror(filename); |
295 | exit(1); |
296 | } |
297 | fp = fdopen(fd, "a"); |
298 | fprintf(fp, "%s@%d:%s %s\n", keytype, port, hostname, key); |
299 | fclose(fp); |
1709795f |
300 | } |
301 | |
302 | void read_random_seed(noise_consumer_t consumer) |
303 | { |
d9c40fd6 |
304 | int fd; |
305 | char fname[FILENAME_MAX]; |
306 | |
307 | make_filename(fname, INDEX_RANDSEED); |
308 | fd = open(fname, O_RDONLY); |
309 | if (fd) { |
310 | char buf[512]; |
311 | int ret; |
312 | while ( (ret = read(fd, buf, sizeof(buf))) > 0) |
313 | consumer(buf, ret); |
314 | close(fd); |
315 | } |
1709795f |
316 | } |
317 | |
318 | void write_random_seed(void *data, int len) |
319 | { |
d9c40fd6 |
320 | int fd; |
321 | char fname[FILENAME_MAX]; |
322 | |
323 | make_filename(fname, INDEX_RANDSEED); |
e3ac3c05 |
324 | /* |
325 | * Don't truncate the random seed file if it already exists; if |
326 | * something goes wrong half way through writing it, it would |
327 | * be better to leave the old data there than to leave it empty. |
328 | */ |
329 | fd = open(fname, O_CREAT | O_WRONLY, 0600); |
d9c40fd6 |
330 | if (fd < 0) { |
331 | char dir[FILENAME_MAX]; |
332 | |
333 | make_filename(dir, INDEX_DIR); |
334 | mkdir(dir, 0700); |
e3ac3c05 |
335 | fd = open(fname, O_CREAT | O_WRONLY, 0600); |
d9c40fd6 |
336 | } |
337 | |
338 | while (len > 0) { |
339 | int ret = write(fd, data, len); |
340 | if (ret <= 0) break; |
341 | len -= ret; |
342 | data = (char *)data + len; |
343 | } |
344 | |
345 | close(fd); |
1709795f |
346 | } |
347 | |
348 | void cleanup_all(void) |
349 | { |
350 | } |