/********** * Copyright (c) 2003-2005 Greg Parker. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY GREG PARKER ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **********/ #include "includes.h" #include "ssh/ssh.h" #include "vt100/vt100.h" #include "rsrc/rsrc.h" #include "crypto/rand.h" #include "armstubs.h" #include "data/connectionlist.h" #include "data/hostkeys.h" #include "data/memolist.h" #include "data/prefs.h" #include "data/publickeys.h" #include "forms/about.h" #include "forms/connectionprefsform.h" #include "forms/detailsform.h" #include "forms/displayprefsform.h" #include "forms/hostkeysform.h" #include "forms/hostkeydetailsform.h" #include "forms/kbdintform.h" #include "forms/kbdint0form.h" #include "forms/keyboardprefsform.h" #include "forms/loginform.h" #include "forms/mainform.h" #include "forms/memoform.h" #include "forms/passwordform.h" #include "forms/passphraseform.h" #include "forms/publickeysform.h" #include "forms/publickeychoiceform.h" #include "forms/publickeydetailsform.h" #include "forms/terminalform.h" #include "forms/DIA.h" #include "crypto/openssl/bn/bn.h" #include "crypto/openssl/md5/md5.h" Err errno; UInt16 NetLibCount = 0; struct ssh_session_t *ss = NULL; // fixme move to recordlist DmOpenRef OpenDB(UInt32 type, char *name, Boolean resDB, Boolean create) { DmOpenRef result; Err err; result = DmOpenDatabaseByTypeCreator(type, PSSH_CREATOR, dmModeReadWrite); if (!result && create) { UInt16 card; LocalID id; UInt16 attr; err = DmCreateDatabase(0, name, PSSH_CREATOR, type, resDB); if (err) { complain_int("db 1", err); return 0; } result = DmOpenDatabaseByTypeCreator(type, PSSH_CREATOR, dmModeReadWrite); if (!result) { complain_int("db 2", DmGetLastErr()); return 0; } // Set backup bit DmOpenDatabaseInfo(result, &id, NULL, NULL, &card, NULL); DmDatabaseInfo(card, id, NULL, &attr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); attr |= dmHdrAttrBackup; DmSetDatabaseInfo(card, id, NULL, &attr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); } return result; } void complain(char *err) { char errnobuf[20]; WinPushDrawState(); WinSetCoordinateSystem(kCoordinatesStandard); FrmCustomAlert(AlertFormID, err, StrIToA(errnobuf, errno), " "); WinPopDrawState(); } void complain_int(char *err, uint32_t i) { char intbuf[20]; WinPushDrawState(); WinSetCoordinateSystem(kCoordinatesStandard); FrmCustomAlert(AlertFormID, err, StrIToA(intbuf, i), " "); WinPopDrawState(); } Boolean ApplicationHandleEvent(EventPtr e) { if (e->eType == frmLoadEvent) { UInt16 formId = e->data.frmLoad.formID; FormPtr frmP = FrmInitForm(formId); FrmSetActiveForm(frmP); switch(formId) { case MainFormID: SetResizePolicy(formId); SetResizeCallback(formId, MainFormResize); FrmSetEventHandler(frmP, MainFormHandleEvent); break; case DetailsFormID: SetResizePolicy(formId); FrmSetEventHandler(frmP, DetailsFormHandleEvent); break; case LoginFormID: SetResizePolicy(formId); FrmSetEventHandler(frmP, LoginFormHandleEvent); break; case PasswordFormID: SetResizePolicy(formId); FrmSetEventHandler(frmP, PasswordFormHandleEvent); break; case TerminalFormID: SetResizePolicy(formId); SetResizeCallback(formId, TerminalFormResize); FrmSetEventHandler(frmP, TerminalFormHandleEvent); break; case HostKeysFormID: SetResizePolicy(formId); SetResizeCallback(formId, HostKeysFormResize); FrmSetEventHandler(frmP, HostKeysFormHandleEvent); break; case HostKeyDetailsFormID: SetResizePolicy(formId); FrmSetEventHandler(frmP, HostKeyDetailsFormHandleEvent); break; case AboutFormID: SetResizePolicy(formId); FrmSetEventHandler(frmP, AboutFormHandleEvent); break; case CreditsFormID: SetResizePolicy(formId); FrmSetEventHandler(frmP, CreditsFormHandleEvent); break; case DisplayPrefsFormID: SetResizePolicy(formId); FrmSetEventHandler(frmP, DisplayPrefsFormHandleEvent); break; case ConnectionPrefsFormID: SetResizePolicy(formId); FrmSetEventHandler(frmP, ConnectionPrefsFormHandleEvent); break; case KeyboardPrefsFormID: SetResizePolicy(formId); FrmSetEventHandler(frmP, KeyboardPrefsFormHandleEvent); break; case MemoFormID: SetResizePolicy(formId); SetResizeCallback(formId, MemoFormResize); FrmSetEventHandler(frmP, MemoFormHandleEvent); break; case PublicKeysFormID: SetResizePolicy(formId); SetResizeCallback(formId, PublicKeysFormResize); FrmSetEventHandler(frmP, PublicKeysFormHandleEvent); break; case PublicKeyDetailsFormID: SetResizePolicy(formId); FrmSetEventHandler(frmP, PublicKeyDetailsFormHandleEvent); break; case PassphraseFormID: SetResizePolicy(formId); FrmSetEventHandler(frmP, PasswordFormHandleEvent); break; case PublicKeyChoiceFormID: SetResizePolicy(formId); FrmSetEventHandler(frmP, PublicKeyChoiceFormHandleEvent); break; case KbdIntFormID: SetResizePolicy(formId); FrmSetEventHandler(frmP, KbdIntFormHandleEvent); break; case KbdInt0FormID: SetResizePolicy(formId); FrmSetEventHandler(frmP, KbdInt0FormHandleEvent); break; case BannerFormID: SetResizePolicy(formId); FrmSetEventHandler(frmP, BannerFormHandleEvent); break; default: break; } return true; } return false; } static Boolean RomVersionCompatible (UInt32 requiredVersion) { UInt32 romVersion; // See if we're on in minimum required version of the ROM or later. // The system records the version number in a feature. A feature is a // piece of information which can be looked up by a creator and feature // number. FtrGet(sysFtrCreator, sysFtrNumROMVersion, &romVersion); return (romVersion >= requiredVersion); } static Boolean CPUIsARM(void) { UInt32 cpu; FtrGet(sysFtrCreator, sysFtrNumProcessorID, &cpu); return sysFtrNumProcessorIsARM(cpu); } static void StartupError(char *msg, UInt16 launchFlags) { UInt32 romVersion; FtrGet(sysFtrCreator, sysFtrNumROMVersion, &romVersion); // If the user launched the app from the launcher, explain // why the app shouldn't run. If the app was contacted for something // else, like it was asked to find a string by the system find, then // don't bother the user with a warning dialog. These flags tell how // the app was launched to decided if a warning should be displayed. if ((launchFlags & (sysAppLaunchFlagNewGlobals | sysAppLaunchFlagUIApp)) == (sysAppLaunchFlagNewGlobals | sysAppLaunchFlagUIApp)) { complain(msg); // Pilot 1.0 will continuously relaunch this app unless we switch to // another safe one. The sysFileCDefaultApp is considered "safe". if (romVersion < 0x02000000) { AppLaunchWithCommand(sysFileCDefaultApp, sysAppLaunchCmdNormalLaunch, NULL); } } } static Boolean NetLibAvailable(void) { Err err; UInt32 version; err = FtrGet(netFtrCreator, netFtrNumVersion, &version); if (err) return false; // any NetLib version is OK err = SysLibFind("Net.lib", &AppNetRefnum); if (err) return false; return true; } UInt32 PilotMain(UInt16 cmd, void *cmdPBP, UInt16 launchFlags) { int loopcount = 0; UInt16 version; int i; UInt32 tenth = SysTicksPerSecond() / 10; UInt32 lastUpdate = 0; UInt32 now; if (cmd == sysAppLaunchCmdNotify) { HandleResizeNotification(((SysNotifyParamType *)cmdPBP)->notifyType); } // we don't handle search et al. if (cmd != sysAppLaunchCmdNormalLaunch) return 0; // ARM CPU required // fixme unless better bnlib is written if (!CPUIsARM()) { StartupError("pssh requires an ARM processor", launchFlags); return 0; } // ROM version 3.5+ required // fixme this should always pass given ARM if (!RomVersionCompatible(sysMakeROMVersion(3,5,0,0,0))) { StartupError("pssh requires ROM version 3.5 or later", launchFlags); return 0; } // NetLib required (any version) if (!NetLibAvailable()) { StartupError("pssh requires NetLib", launchFlags); return 0; } if (!init_arm()) { StartupError("Couldn't load ARM code", launchFlags); return 0; } if (!ConnectionListInit()) { StartupError("Couldn't open connection list", launchFlags); return 0; } if (!HostKeysInit()) { StartupError("Couldn't open known host key list", launchFlags); return 0; } if (!PublicKeysInit()) { StartupError("Couldn't open public key list", launchFlags); return 0; } if (!PrefsInit()) { StartupError("Couldn't open preferences", launchFlags); return 0; } { uint32_t warned; Err err; err = FtrGet(PSSH_CREATOR, ftrSecurityWarning, &warned); if (err) warned = 0; if (!warned) { FrmCustomAlert(AlertFormID, "WARNING: pssh is substantially UNTESTED and probably INSECURE. Do not use for security-critical applications.", " ", " "); warned = 1; FtrSet(PSSH_CREATOR, ftrSecurityWarning, warned); } } // init virtual Graffiti area API, if any InitializeResizeSupport(ResizeDataID); LoadResizePrefs(PSSH_CREATOR, ResizeDataID); RAND_init(); /* { // fixme quickie dynamic heap check UInt16 heapID; UInt32 free; UInt32 max; UInt32 size; heapID = MemHeapID(0, 0); MemHeapFreeBytes(heapID, &free, &max); size = MemHeapSize(heapID); complain_int("dynamic heap size ", size); complain_int("dynamic heap free ", free); complain_int("dynamic heap max chunk ", max); } */ { MemHandle fontH; FontType *fontP; #define DEFINEFONT(f) \ fontH = DmGetResource(fontExtRscType, f); \ fontP = MemHandleLock(fontH); \ FntDefineFont((FontID)f, fontP); DEFINEFONT(NanoFontSingleID); DEFINEFONT(NanoFontDoubleID); DEFINEFONT(MediumFontSingleID); DEFINEFONT(MediumFontDoubleID); DEFINEFONT(PasswordFontID); #undef DEFINEFONT } // 10 second timeout for DNS and connect() AppNetTimeout = SysTicksPerSecond() * 10; FrmGotoForm(MainFormID); do { Int32 delay; int lastEventType; if (ss && !ssh_is_closed(ss)) { fd_set rfds; int maxfd; int ssfd; int selected; struct timeval tv; FD_ZERO(&rfds); FD_SET(STDIN_FILENO, &rfds); ssfd = ssh_request_select(ss, &rfds, NULL); maxfd = MAX(STDIN_FILENO, ssfd); // Don't block in select() if there are events in the event queue. // fixme even if there are no events, keep block time short // because of stuck-in-select problem if (EvtEventAvail()) { tv.tv_usec = 0; tv.tv_sec = 0; } else { tv.tv_usec = 100000L; // 1/10 sec tv.tv_sec = 0; } selected = select(maxfd + 1, &rfds, NULL, NULL, &tv); if (selected >= 0) { if (FD_ISSET(ssfd, &rfds)) { EventType e = {0}; e.eType = usrNetEvent; EvtAddUniqueEventToQueue(&e, 0, true); } } delay = 0; // DON'T block in EvtGetEvent } else { // No sockets to select() yet delay = -1; // DO block in EvtGetEvent (forever) } { EventType event; Err err; EvtGetEvent(&event, delay); if (event.eType == usrNetEvent) { // Handle incoming network data if (ss && !ssh_is_closed(ss)) { ssh_read(ss); } } if (TerminalFormStealEvent(&event)) { FrmDispatchEvent(&event); } else { if (!SysHandleEvent(&event)) { if (!MenuHandleEvent(NULL, &event, &err)) { if (!ApplicationHandleEvent(&event)) { FrmDispatchEvent(&event); } } } } if (ss) { now = TimGetTicks(); if (now >= lastUpdate + tenth) { ssh_task(ss); lastUpdate = now; } } if (event.eType == keyDownEvent || event.eType == keyUpEvent || event.eType == penUpEvent || event.eType == penMoveEvent) { RAND_add_event_entropy(&event); } if (event.eType == appStopEvent) break; } } while (1); done: FrmSaveAllForms(); FrmCloseAllForms(); MemoListFree(); PublicKeysFree(); HostKeysFree(); ConnectionListFree(); for (i = 0; i < NetLibCount; i++) { NetLibClose(AppNetRefnum, false); } SaveResizePrefs(PSSH_CREATOR, ResizeDataID, 0); TerminateResizeSupport(); RAND_stop(); PealUnload(arm_module); return 0; } /* #if 0 // memory leak tracing { static int inited = 0; static UInt16 heapID; static uint32_t gen = 0; UInt32 free; UInt32 max; UInt32 size; extern uint32_t ev_block_count; extern uint32_t ev_byte_count; // extern uint32_t ev_blocks_used; if (!inited) { inited = 1; heapID = MemHeapID(0, 0); } MemHeapFreeBytes(heapID, &free, &max); size = MemHeapSize(heapID); debug_printf("mem %luK,%lu[%luK]AB,%lu gen", (size-free)/1024, ev_block_count, ev_byte_count/1024, gen++); // if (gen % 100 == 0) arena_dump_blocks(); } #endif // fixme write queue //if (ssh_is_selected(ss, &wfds)) { // ssh_write(ss); // } */