2 * Copyright (c) 2003-2004 Greg Parker. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY GREG PARKER ``AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 #include "recordlist.h"
27 #include "rsrc/rsrc.h"
28 #include "ssh/openssh/key.h"
29 #include "ssh/openssh/match.h"
34 /* host key record format:
37 n bytes hostname,hostname,hostname\0
43 #define HostKeyDBName "pssh Known Host Keys"
44 #define HostKeyDBType 'HKey'
45 static DmOpenRef HostKeyDB = 0;
46 static RecordList *HostKeyList = NULL;
48 static Boolean ReadHostKeyRecord(uint8_t *recordP, char **hostnames, uint8_t **keyblob, uint16_t *keybloblen) HOSTKEYS_SEGMENT;
49 static Boolean WriteHostKeyRecord(MemPtr recordP, const char *hostnames, uint8_t *keyblob, uint16_t keybloblen) HOSTKEYS_SEGMENT;
50 static void DrawHostKeyRecord(MemPtr recordP, UInt16 index, RectanglePtr bounds) HOSTKEYS_SEGMENT;
52 extern DmOpenRef OpenDB(UInt32 type, char *name, Boolean resDB, Boolean create);
54 Boolean HostKeysInit(void)
56 HostKeyDB = OpenDB(HostKeyDBType, HostKeyDBName, false, true);
57 if (!HostKeyDB) return false;
60 RecordListNew(HostKeyDB, HostKeysFormID, HostKeysFormKeyTableID,
61 HostKeysFormKeyScrollbarID, DrawHostKeyRecord);
62 if (!HostKeyList) return false;
68 void HostKeysFree(void)
70 RecordListFree(HostKeyList);
71 DmCloseDatabase(HostKeyDB);
75 void HostKeysUpdate(void)
77 return RecordListUpdate(HostKeyList);
81 Boolean HostKeysHandleEvent(EventPtr event)
83 return RecordListHandleEvent(HostKeyList, event);
87 UInt16 HostKeysSelectedIndex(void)
89 return RecordListSelectedIndex(HostKeyList);
93 void HostKeysDeleteSelectedRecord(void)
95 return RecordListDeleteSelectedRecord(HostKeyList);
99 static Boolean ReadHostKeyRecord(uint8_t *recordP, char **hostnames,
100 uint8_t **keyblob, uint16_t *keybloblen)
102 #define CHECK_SPACE(n) do { if (p+(n)>end) goto bad; } while (0)
109 end = recordP + MemPtrSize(recordP);
111 // read hostnames string
114 len = *(uint16_t *)p;
117 *hostnames = (char *)p;
118 if ((*hostnames)[len-1] != '\0') goto bad;
124 *keybloblen = *(uint16_t *)p;
126 CHECK_SPACE(*keybloblen);
131 // allow trailing data for forward compatibility
142 static Boolean WriteHostKeyRecord(MemPtr recordP, const char *hostnames,
143 uint8_t *keyblob, uint16_t keybloblen)
147 uint16_t hostnameslen = strlen(hostnames) + 1;
149 if (!err) err = DmWrite(recordP, offset, &hostnameslen, 2);
151 if (!err) err = DmWrite(recordP, offset, hostnames, hostnameslen);
152 offset += hostnameslen;
153 if (!err) err = DmWrite(recordP, offset, &keybloblen, 2);
155 if (!err) err = DmWrite(recordP, offset, keyblob, keybloblen);
162 MemHandle HostKeysQuerySelectedRecord(char **hostnames,
163 uint8_t **keyblob, uint16_t *keybloblen)
165 return HostKeysQueryIndexedRecord(RecordListSelectedIndex(HostKeyList),
166 hostnames, keyblob, keybloblen);
170 MemHandle HostKeysQueryIndexedRecord(UInt16 index, char **hostnames,
171 uint8_t **keyblob, uint16_t *keybloblen)
177 recordH = RecordListQueryIndexedRecord(HostKeyList, index);
178 if (!recordH) return NULL;
180 recordP = MemHandleLock(recordH);
181 ok = ReadHostKeyRecord(recordP, hostnames, keyblob, keybloblen);
184 MemHandleUnlock(recordH);
192 UInt16 HostKeysFindRecordForHostname(const char *hostname)
194 UInt16 count = RecordListCount(HostKeyList);
201 for (index = 0; index < count; index++) {
202 if ((recordH = HostKeysQueryIndexedRecord(index, &savedhosts,
203 &keyblob, &keybloblen)))
205 char *host = match_list(hostname, savedhosts, NULL);
206 MemHandleUnlock(recordH);
218 UInt16 HostKeysFindRecordForKey(Key *hostkey)
220 UInt16 count = RecordListCount(HostKeyList);
227 for (index = 0; index < count; index++) {
228 if ((recordH = HostKeysQueryIndexedRecord(index, &savedhosts,
229 &keyblob, &keybloblen)))
231 Key *savedkey = key_from_blob(keyblob, keybloblen);
232 Boolean match = key_equal(savedkey, hostkey);
234 MemHandleUnlock(recordH);
236 if (match) return index;
244 Boolean HostKeysAddRecord(const char *hostname, Key *hostkey)
246 uint16_t keybloblen = 1;
253 key_to_blob(hostkey, &keyblob, &keybloblen);
255 recordlen = 2L + strlen(hostname) + 1 + 2 + keybloblen;
256 RecordListClearSelection(HostKeyList);
257 recordH = RecordListGetSelectedRecord(HostKeyList, recordlen);
263 recordP = MemHandleLock(recordH);
265 ok = WriteHostKeyRecord(recordP, hostname, keyblob, keybloblen);
267 // fixme delete new record on failure
269 MemHandleUnlock(recordH);
270 RecordListReleaseRecord(HostKeyList, recordH, true);
276 Boolean HostKeysAddHostnameToRecord(const char *hostname, UInt16 index)
284 Boolean result = false;
289 RecordListClearSelection(HostKeyList);
290 RecordListSetSelectedIndex(HostKeyList, index);
292 recordH = RecordListQuerySelectedRecord(HostKeyList);
293 if (!recordH) return false;
295 // new length includes comma and new hostname
296 // (assumes record contains at least 1 hostname, and that
297 // the new hostname isn't there already)
298 recordlen = MemHandleSize(recordH) + strlen(hostname) + strlen(",");
300 recordH = DmResizeRecord(HostKeyDB, index, recordlen);
301 if (!recordH) return false;
302 recordP = MemHandleLock(recordH);
304 ok = ReadHostKeyRecord(recordP, &hostnames, &keyblob, &keybloblen);
307 newhostnames = arena_malloc(strlen(hostnames) + strlen(",") + strlen(hostname) + 1);
308 strcpy(newhostnames, hostnames);
309 strcat(newhostnames, ",");
310 strcat(newhostnames, hostname);
312 newkeyblob = arena_malloc(keybloblen);
313 memcpy(newkeyblob, keyblob, keybloblen);
315 ok = WriteHostKeyRecord(recordP, newhostnames, newkeyblob, keybloblen);
316 arena_free(newhostnames);
317 arena_free(newkeyblob);
323 // fixme destroy record on failure
324 MemHandleUnlock(recordH);
325 RecordListReleaseRecord(HostKeyList, recordH, true);
331 Boolean HostKeysRemoveHostnameFromRecord(const char *hostname, UInt16 index)
344 RecordListClearSelection(HostKeyList);
345 RecordListSetSelectedIndex(HostKeyList, index);
347 recordH = RecordListQuerySelectedRecord(HostKeyList);
348 if (!recordH) return false;
349 recordP = MemHandleLock(recordH);
351 ok = ReadHostKeyRecord(recordP, &hostnames, &keyblob, &keybloblen);
353 MemHandleUnlock(recordH);
357 // If this is the only hostname in the record - kill it completely
358 if (0 == strcasecmp(hostnames, hostname)) {
359 MemHandleUnlock(recordH);
360 RecordListDeleteSelectedRecord(HostKeyList);
364 // Make a copy of newhostnames that does not include hostname
365 newhostnames = arena_strdup(hostnames);
368 // try ...,hostname,...
369 char *commahostnamecomma = arena_malloc(1 + strlen(hostname) + 1 + 1);
370 strcpy(commahostnamecomma, ",");
371 strcat(commahostnamecomma, hostname);
372 strcat(commahostnamecomma, ",");
373 start = strcasestr(newhostnames, commahostnamecomma);
374 if (start) end = start + strlen(commahostnamecomma);
375 arena_free(commahostnamecomma);
379 char *hostnamecomma = arena_malloc(strlen(hostname) + 1 + 1);
380 strcpy(hostnamecomma, hostname);
381 strcat(hostnamecomma, ",");
382 if (0 == strncmp(newhostnames, hostnamecomma, strlen(hostnamecomma))) {
383 start = newhostnames;
384 end = start + strlen(hostnamecomma);
386 arena_free(hostnamecomma);
390 char *commahostname = arena_malloc(1 + strlen(hostname) + 1);
391 strcpy(commahostname, ",");
392 strcat(commahostname, hostname);
393 start = strrchr(newhostnames, ',');
394 if (start && 0 == strcmp(start, commahostname)) {
395 end = start + strlen(commahostname);
397 arena_free(commahostname);
400 // didn't find hostname in hostname list
401 MemHandleUnlock(recordH);
402 arena_free(newhostnames);
407 memmove(start, end, strlen(end)+1);
409 newkeyblob = arena_malloc(keybloblen);
410 memcpy(newkeyblob, keyblob, keybloblen);
412 recordlen = 2L + strlen(newhostnames) + 1 + 2 + keybloblen;
414 // Reopen the record for writing and resize
415 MemHandleUnlock(recordH);
416 recordH = DmResizeRecord(HostKeyDB, RecordListSelectedIndex(HostKeyList),
419 arena_free(newhostnames);
420 arena_free(newkeyblob);
424 // Write the new record data
425 recordP = MemHandleLock(recordH);
426 ok = WriteHostKeyRecord(recordP, newhostnames, newkeyblob, keybloblen);
427 MemHandleUnlock(recordH);
428 RecordListReleaseRecord(HostKeyList, recordH, true);
429 arena_free(newhostnames);
430 arena_free(newkeyblob);
431 if (!ok) return false;
437 static void DrawHostKeyRecord(MemPtr recordP, UInt16 index,
444 if (ReadHostKeyRecord(recordP, &hostnames,
445 &keyblob, &keybloblen))
447 // "hostname,hostname,hostname"
449 int x = bounds->topLeft.x + 1;
450 int y = bounds->topLeft.y;
452 len = StrLen(hostnames);
453 WinDrawTruncChars(hostnames, len, x, y,
454 bounds->topLeft.x + bounds->extent.x - x - 1);
455 x += FntCharsWidth(hostnames, len);