1 /*
2  * SSH server for Unix: main program.
3  *
4  * ======================================================================
5  *
6  * This server is NOT SECURE!
7  *
8  * DO NOT DEPLOY IT IN A HOSTILE-FACING ENVIRONMENT!
9  *
10  * Its purpose is to speak the server end of everything PuTTY speaks
11  * on the client side, so that I can test that I haven't broken PuTTY
12  * when I reorganise its code, even things like RSA key exchange or
13  * chained auth methods which it's hard to find a server that speaks
14  * at all.
15  *
16  * It has no interaction with the OS's authentication system: the
17  * authentications it will accept are configurable by command-line
18  * option, and once you authenticate, it will run the connection
19  * protocol - including all subprocesses and shells - under the same
20  * Unix user id you started it under.
21  *
22  * It really is only suitable for testing the actual SSH protocol.
23  * Don't use it for anything more serious!
24  *
25  * ======================================================================
26  */
27 
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <errno.h>
31 #include <assert.h>
32 #include <stdarg.h>
33 #include <signal.h>
34 #include <unistd.h>
35 #include <fcntl.h>
36 #include <termios.h>
37 #include <pwd.h>
38 #include <sys/ioctl.h>
39 #include <sys/time.h>
40 
41 #include "putty.h"
42 #include "mpint.h"
43 #include "ssh.h"
44 #include "sshserver.h"
45 
46 const char *const appname = "uppity";
47 
modalfatalbox(const char * p,...)48 void modalfatalbox(const char *p, ...)
49 {
50     va_list ap;
51     fprintf(stderr, "FATAL ERROR: ");
52     va_start(ap, p);
53     vfprintf(stderr, p, ap);
54     va_end(ap);
55     fputc('\n', stderr);
56     exit(1);
57 }
nonfatal(const char * p,...)58 void nonfatal(const char *p, ...)
59 {
60     va_list ap;
61     fprintf(stderr, "ERROR: ");
62     va_start(ap, p);
63     vfprintf(stderr, p, ap);
64     va_end(ap);
65     fputc('\n', stderr);
66 }
67 
platform_default_s(const char * name)68 char *platform_default_s(const char *name)
69 {
70     return NULL;
71 }
72 
platform_default_b(const char * name,bool def)73 bool platform_default_b(const char *name, bool def)
74 {
75     return def;
76 }
77 
platform_default_i(const char * name,int def)78 int platform_default_i(const char *name, int def)
79 {
80     return def;
81 }
82 
platform_default_fontspec(const char * name)83 FontSpec *platform_default_fontspec(const char *name)
84 {
85     return fontspec_new("");
86 }
87 
platform_default_filename(const char * name)88 Filename *platform_default_filename(const char *name)
89 {
90     return filename_from_str("");
91 }
92 
x_get_default(const char * key)93 char *x_get_default(const char *key)
94 {
95     return NULL;                       /* this is a stub */
96 }
97 
old_keyfile_warning(void)98 void old_keyfile_warning(void) { }
99 
timer_change_notify(unsigned long next)100 void timer_change_notify(unsigned long next)
101 {
102 }
103 
platform_get_x_display(void)104 char *platform_get_x_display(void) { return NULL; }
105 
make_unix_sftp_filehandle_key(void * data,size_t size)106 void make_unix_sftp_filehandle_key(void *data, size_t size)
107 {
108     random_read(data, size);
109 }
110 
111 static bool verbose;
112 
113 struct AuthPolicyShared {
114     struct AuthPolicy_ssh1_pubkey *ssh1keys;
115     struct AuthPolicy_ssh2_pubkey *ssh2keys;
116 };
117 
118 struct AuthPolicy {
119     struct AuthPolicyShared *shared;
120     int kbdint_state;
121 };
122 
123 struct server_instance {
124     unsigned id;
125     AuthPolicy ap;
126     LogPolicy logpolicy;
127 };
128 
log_to_stderr(unsigned id,const char * msg)129 static void log_to_stderr(unsigned id, const char *msg)
130 {
131     if (id != (unsigned)-1)
132         fprintf(stderr, "#%u: ", id);
133     fputs(msg, stderr);
134     fputc('\n', stderr);
135     fflush(stderr);
136 }
137 
server_eventlog(LogPolicy * lp,const char * event)138 static void server_eventlog(LogPolicy *lp, const char *event)
139 {
140     struct server_instance *inst = container_of(
141         lp, struct server_instance, logpolicy);
142     if (verbose)
143         log_to_stderr(inst->id, event);
144 }
145 
server_logging_error(LogPolicy * lp,const char * event)146 static void server_logging_error(LogPolicy *lp, const char *event)
147 {
148     struct server_instance *inst = container_of(
149         lp, struct server_instance, logpolicy);
150     log_to_stderr(inst->id, event);    /* unconditional */
151 }
152 
server_askappend(LogPolicy * lp,Filename * filename,void (* callback)(void * ctx,int result),void * ctx)153 static int server_askappend(
154     LogPolicy *lp, Filename *filename,
155     void (*callback)(void *ctx, int result), void *ctx)
156 {
157     return 2; /* always overwrite (FIXME: could make this a cmdline option) */
158 }
159 
160 static const LogPolicyVtable server_logpolicy_vt = {
161     .eventlog = server_eventlog,
162     .askappend = server_askappend,
163     .logging_error = server_logging_error,
164     .verbose = null_lp_verbose_no,
165 };
166 
167 struct AuthPolicy_ssh1_pubkey {
168     RSAKey key;
169     struct AuthPolicy_ssh1_pubkey *next;
170 };
171 struct AuthPolicy_ssh2_pubkey {
172     ptrlen public_blob;
173     struct AuthPolicy_ssh2_pubkey *next;
174 };
175 
auth_methods(AuthPolicy * ap)176 unsigned auth_methods(AuthPolicy *ap)
177 {
178     return (AUTHMETHOD_PUBLICKEY | AUTHMETHOD_PASSWORD | AUTHMETHOD_KBDINT |
179             AUTHMETHOD_TIS | AUTHMETHOD_CRYPTOCARD);
180 }
auth_none(AuthPolicy * ap,ptrlen username)181 bool auth_none(AuthPolicy *ap, ptrlen username)
182 {
183     return false;
184 }
auth_password(AuthPolicy * ap,ptrlen username,ptrlen password,ptrlen * new_password_opt)185 int auth_password(AuthPolicy *ap, ptrlen username, ptrlen password,
186                   ptrlen *new_password_opt)
187 {
188     const char *PHONY_GOOD_PASSWORD = "weasel";
189     const char *PHONY_BAD_PASSWORD = "ferret";
190 
191     if (!new_password_opt) {
192         /* Accept login with our preconfigured good password */
193         if (ptrlen_eq_string(password, PHONY_GOOD_PASSWORD))
194             return 1;
195         /* Don't outright reject the bad password, but insist on a change */
196         if (ptrlen_eq_string(password, PHONY_BAD_PASSWORD))
197             return 2;
198         /* Reject anything else */
199         return 0;
200     } else {
201         /* In a password-change request, expect the bad password as input */
202         if (!ptrlen_eq_string(password, PHONY_BAD_PASSWORD))
203             return 0;
204         /* Accept a request to change it to the good password */
205         if (ptrlen_eq_string(*new_password_opt, PHONY_GOOD_PASSWORD))
206             return 1;
207         /* Outright reject a request to change it to the same password
208          * as it already 'was' */
209         if (ptrlen_eq_string(*new_password_opt, PHONY_BAD_PASSWORD))
210             return 0;
211         /* Anything else, pretend the new pw wasn't good enough, and
212          * re-request a change */
213         return 2;
214     }
215 }
auth_publickey(AuthPolicy * ap,ptrlen username,ptrlen public_blob)216 bool auth_publickey(AuthPolicy *ap, ptrlen username, ptrlen public_blob)
217 {
218     struct AuthPolicy_ssh2_pubkey *iter;
219     for (iter = ap->shared->ssh2keys; iter; iter = iter->next) {
220         if (ptrlen_eq_ptrlen(public_blob, iter->public_blob))
221             return true;
222     }
223     return false;
224 }
auth_publickey_ssh1(AuthPolicy * ap,ptrlen username,mp_int * rsa_modulus)225 RSAKey *auth_publickey_ssh1(
226     AuthPolicy *ap, ptrlen username, mp_int *rsa_modulus)
227 {
228     struct AuthPolicy_ssh1_pubkey *iter;
229     for (iter = ap->shared->ssh1keys; iter; iter = iter->next) {
230         if (mp_cmp_eq(rsa_modulus, iter->key.modulus))
231             return &iter->key;
232     }
233     return NULL;
234 }
auth_kbdint_prompts(AuthPolicy * ap,ptrlen username)235 AuthKbdInt *auth_kbdint_prompts(AuthPolicy *ap, ptrlen username)
236 {
237     AuthKbdInt *aki;
238 
239     switch (ap->kbdint_state) {
240       case 0:
241         aki = snew(AuthKbdInt);
242         aki->title = dupstr("Initial double prompt");
243         aki->instruction =
244             dupstr("First prompt should echo, second should not");
245         aki->nprompts = 2;
246         aki->prompts = snewn(aki->nprompts, AuthKbdIntPrompt);
247         aki->prompts[0].prompt = dupstr("Echoey prompt: ");
248         aki->prompts[0].echo = true;
249         aki->prompts[1].prompt = dupstr("Silent prompt: ");
250         aki->prompts[1].echo = false;
251         return aki;
252       case 1:
253         aki = snew(AuthKbdInt);
254         aki->title = dupstr("Zero-prompt step");
255         aki->instruction = dupstr("Shouldn't see any prompts this time");
256         aki->nprompts = 0;
257         aki->prompts = NULL;
258         return aki;
259       default:
260         ap->kbdint_state = 0;
261         return NULL;
262     }
263 }
auth_kbdint_responses(AuthPolicy * ap,const ptrlen * responses)264 int auth_kbdint_responses(AuthPolicy *ap, const ptrlen *responses)
265 {
266     switch (ap->kbdint_state) {
267       case 0:
268         if (ptrlen_eq_string(responses[0], "stoat") &&
269             ptrlen_eq_string(responses[1], "weasel")) {
270             ap->kbdint_state++;
271             return 0;                  /* those are the expected responses */
272         } else {
273             ap->kbdint_state = 0;
274             return -1;
275         }
276         break;
277       case 1:
278         return +1;                     /* succeed after the zero-prompt step */
279       default:
280         ap->kbdint_state = 0;
281         return -1;
282     }
283 }
auth_ssh1int_challenge(AuthPolicy * ap,unsigned method,ptrlen username)284 char *auth_ssh1int_challenge(AuthPolicy *ap, unsigned method, ptrlen username)
285 {
286     /* FIXME: test returning a challenge string without \n, and ensure
287      * it gets printed as a prompt in its own right, without PuTTY
288      * making up a "Response: " prompt to follow it */
289     return dupprintf("This is a dummy %s challenge!\n",
290                      (method == AUTHMETHOD_TIS ? "TIS" : "CryptoCard"));
291 }
auth_ssh1int_response(AuthPolicy * ap,ptrlen response)292 bool auth_ssh1int_response(AuthPolicy *ap, ptrlen response)
293 {
294     return ptrlen_eq_string(response, "otter");
295 }
auth_successful(AuthPolicy * ap,ptrlen username,unsigned method)296 bool auth_successful(AuthPolicy *ap, ptrlen username, unsigned method)
297 {
298     return true;
299 }
300 
safety_warning(FILE * fp)301 static void safety_warning(FILE *fp)
302 {
303     fputs("  =================================================\n"
304           "     THIS SSH SERVER IS NOT WRITTEN TO BE SECURE!\n"
305           "  DO NOT DEPLOY IT IN A HOSTILE-FACING ENVIRONMENT!\n"
306           "  =================================================\n", fp);
307 }
308 
show_help(FILE * fp)309 static void show_help(FILE *fp)
310 {
311     safety_warning(fp);
312     fputs("\n"
313           "usage:   uppity [options]\n"
314           "options: --listen [PORT|PATH] listen to a port on localhost, or Unix socket\n"
315           "         --listen-once        (with --listen) stop after one "
316           "connection\n"
317           "         --hostkey KEY        SSH host key (need at least one)\n"
318           "         --rsakexkey KEY      key for SSH-2 RSA key exchange "
319           "(in SSH-1 format)\n"
320           "         --userkey KEY        public key"
321            " acceptable for user authentication\n"
322           "         --sessiondir DIR     cwd for session subprocess (default $HOME)\n"
323           "         --bannertext TEXT    send TEXT as SSH-2 auth banner\n"
324           "         --bannerfile FILE    send contents of FILE as SSH-2 auth "
325           "banner\n"
326           "         --kexinit-kex STR    override list of SSH-2 KEX methods\n"
327           "         --kexinit-hostkey STR  override list of SSH-2 host key "
328           "types\n"
329           "         --kexinit-cscipher STR override list of SSH-2 "
330           "client->server ciphers\n"
331           "         --kexinit-sccipher STR override list of SSH-2 "
332           "server->client ciphers\n"
333           "         --kexinit-csmac STR    override list of SSH-2 "
334           "client->server MACs\n"
335           "         --kexinit-scmac STR    override list of SSH-2 "
336           "server->client MACs\n"
337           "         --kexinit-cscomp STR   override list of SSH-2 "
338           "c->s compression types\n"
339           "         --kexinit-sccomp STR   override list of SSH-2 "
340           "s->c compression types\n"
341           "         --ssh1-ciphers STR     override list of SSH-1 ciphers\n"
342           "         --ssh1-no-compression  forbid compression in SSH-1\n"
343           "         --exitsignum         send buggy numeric \"exit-signal\" "
344           "message\n"
345           "         --verbose            print event log messages to standard "
346           "error\n"
347           "         --sshlog FILE        write SSH packet log to FILE\n"
348           "         --sshrawlog FILE     write SSH packets + raw data log"
349           " to FILE\n"
350           "also:    uppity --help        show this text\n"
351           "         uppity --version     show version information\n"
352           "\n", fp);
353     safety_warning(fp);
354 }
355 
show_version_and_exit(void)356 static void show_version_and_exit(void)
357 {
358     char *buildinfo_text = buildinfo("\n");
359     printf("%s: %s\n%s\n", appname, ver, buildinfo_text);
360     sfree(buildinfo_text);
361     exit(0);
362 }
363 
364 const bool buildinfo_gtk_relevant = false;
365 
366 static bool listening = false, listen_once = false;
367 static bool finished = false;
server_instance_terminated(LogPolicy * lp)368 void server_instance_terminated(LogPolicy *lp)
369 {
370     struct server_instance *inst = container_of(
371         lp, struct server_instance, logpolicy);
372 
373     if (listening && !listen_once) {
374         log_to_stderr(inst->id, "connection terminated");
375     } else {
376         finished = true;
377     }
378 
379     sfree(inst);
380 }
381 
longoptarg(const char * arg,const char * expected,const char ** val,int * argcp,char *** argvp)382 static bool longoptarg(const char *arg, const char *expected,
383                        const char **val, int *argcp, char ***argvp)
384 {
385     int len = strlen(expected);
386     if (memcmp(arg, expected, len))
387         return false;
388     if (arg[len] == '=') {
389         *val = arg + len + 1;
390         return true;
391     } else if (arg[len] == '\0') {
392         if (--*argcp > 0) {
393             *val = *++*argvp;
394             return true;
395         } else {
396             fprintf(stderr, "%s: option %s expects an argument\n",
397                     appname, expected);
398             exit(1);
399         }
400     }
401     return false;
402 }
403 
longoptnoarg(const char * arg,const char * expected)404 static bool longoptnoarg(const char *arg, const char *expected)
405 {
406     int len = strlen(expected);
407     if (memcmp(arg, expected, len))
408         return false;
409     if (arg[len] == '=') {
410         fprintf(stderr, "%s: option %s expects no argument\n",
411                 appname, expected);
412         exit(1);
413     } else if (arg[len] == '\0') {
414         return true;
415     }
416     return false;
417 }
418 
419 struct server_config {
420     Conf *conf;
421     const SshServerConfig *ssc;
422 
423     ssh_key **hostkeys;
424     int nhostkeys;
425 
426     RSAKey *hostkey1;
427 
428     struct AuthPolicyShared *ap_shared;
429 
430     unsigned next_id;
431 
432     Socket *listening_socket;
433     Plug listening_plug;
434 };
435 
server_conn_plug(struct server_config * cfg,struct server_instance ** inst_out)436 static Plug *server_conn_plug(
437     struct server_config *cfg, struct server_instance **inst_out)
438 {
439     struct server_instance *inst = snew(struct server_instance);
440 
441     memset(inst, 0, sizeof(*inst));
442 
443     inst->id = cfg->next_id++;
444     inst->ap.shared = cfg->ap_shared;
445     inst->logpolicy.vt = &server_logpolicy_vt;
446 
447     if (inst_out)
448         *inst_out = inst;
449 
450     return ssh_server_plug(
451         cfg->conf, cfg->ssc, cfg->hostkeys, cfg->nhostkeys, cfg->hostkey1,
452         &inst->ap, &inst->logpolicy, &unix_live_sftpserver_vt);
453 }
454 
server_log(Plug * plug,PlugLogType type,SockAddr * addr,int port,const char * error_msg,int error_code)455 static void server_log(Plug *plug, PlugLogType type, SockAddr *addr, int port,
456                        const char *error_msg, int error_code)
457 {
458     log_to_stderr((unsigned)-1, error_msg);
459 }
460 
server_closing(Plug * plug,const char * error_msg,int error_code,bool calling_back)461 static void server_closing(Plug *plug, const char *error_msg, int error_code,
462                            bool calling_back)
463 {
464     log_to_stderr((unsigned)-1, error_msg);
465 }
466 
server_accepting(Plug * p,accept_fn_t constructor,accept_ctx_t ctx)467 static int server_accepting(Plug *p, accept_fn_t constructor, accept_ctx_t ctx)
468 {
469     struct server_config *cfg = container_of(
470         p, struct server_config, listening_plug);
471     Socket *s;
472     const char *err;
473 
474     struct server_instance *inst;
475 
476     if (listen_once) {
477         if (!cfg->listening_socket) /* in case of rapid double-accept */
478             return 1;
479         sk_close(cfg->listening_socket);
480         cfg->listening_socket = NULL;
481     }
482 
483     unsigned old_next_id = cfg->next_id;
484 
485     Plug *plug = server_conn_plug(cfg, &inst);
486     s = constructor(ctx, plug);
487     if ((err = sk_socket_error(s)) != NULL)
488         return 1;
489 
490     SocketPeerInfo *pi = sk_peer_info(s);
491 
492     if (pi->addressfamily != ADDRTYPE_LOCAL && !sk_peer_trusted(s)) {
493         fprintf(stderr, "rejected connection from %s (untrustworthy peer)\n",
494                 pi->log_text);
495         sk_free_peer_info(pi);
496         sk_close(s);
497         cfg->next_id = old_next_id;
498         return 1;
499     }
500 
501     char *msg = dupprintf("new connection from %s", pi->log_text);
502     log_to_stderr(inst->id, msg);
503     sfree(msg);
504     sk_free_peer_info(pi);
505 
506     sk_set_frozen(s, false);
507     ssh_server_start(plug, s);
508     return 0;
509 }
510 
511 static const PlugVtable server_plugvt = {
512     .log = server_log,
513     .closing = server_closing,
514     .accepting = server_accepting,
515 };
516 
main(int argc,char ** argv)517 int main(int argc, char **argv)
518 {
519     int listen_port = -1;
520     const char *listen_socket = NULL;
521 
522     ssh_key **hostkeys = NULL;
523     size_t nhostkeys = 0, hostkeysize = 0;
524     RSAKey *hostkey1 = NULL;
525 
526     struct AuthPolicyShared aps;
527     SshServerConfig ssc;
528 
529     Conf *conf = make_ssh_server_conf();
530 
531     aps.ssh1keys = NULL;
532     aps.ssh2keys = NULL;
533 
534     memset(&ssc, 0, sizeof(ssc));
535 
536     ssc.application_name = "Uppity";
537     ssc.session_starting_dir = getenv("HOME");
538     ssc.ssh1_cipher_mask = SSH1_SUPPORTED_CIPHER_MASK;
539     ssc.ssh1_allow_compression = true;
540 
541     if (argc <= 1) {
542         /*
543          * We're going to terminate with an error message below,
544          * because there are no host keys. But we'll display the help
545          * as additional standard-error output, if nothing else so
546          * that people see the giant safety warning.
547          */
548         show_help(stderr);
549         fputc('\n', stderr);
550     }
551 
552     while (--argc > 0) {
553         const char *arg = *++argv;
554         const char *val;
555 
556         if (!strcmp(arg, "--help")) {
557             show_help(stdout);
558             exit(0);
559         } else if (longoptnoarg(arg, "--version")) {
560             show_version_and_exit();
561         } else if (longoptnoarg(arg, "--verbose") || !strcmp(arg, "-v")) {
562             verbose = true;
563         } else if (longoptarg(arg, "--listen", &val, &argc, &argv)) {
564             if (val[0] == '/') {
565                 listen_port = -1;
566                 listen_socket = val;
567             } else {
568                 listen_port = atoi(val);
569                 listen_socket = NULL;
570             }
571         } else if (!strcmp(arg, "--listen-once")) {
572             listen_once = true;
573         } else if (longoptarg(arg, "--hostkey", &val, &argc, &argv)) {
574             Filename *keyfile;
575             int keytype;
576             const char *error;
577 
578             keyfile = filename_from_str(val);
579             keytype = key_type(keyfile);
580 
581             if (keytype == SSH_KEYTYPE_SSH2) {
582                 ssh2_userkey *uk;
583                 ssh_key *key;
584                 uk = ppk_load_f(keyfile, NULL, &error);
585                 filename_free(keyfile);
586                 if (!uk || !uk->key) {
587                     fprintf(stderr, "%s: unable to load host key '%s': "
588                             "%s\n", appname, val, error);
589                     exit(1);
590                 }
591                 char *invalid = ssh_key_invalid(uk->key, 0);
592                 if (invalid) {
593                     fprintf(stderr, "%s: host key '%s' is unusable: "
594                             "%s\n", appname, val, invalid);
595                     exit(1);
596                 }
597                 key = uk->key;
598                 sfree(uk->comment);
599                 sfree(uk);
600 
601                 for (int i = 0; i < nhostkeys; i++)
602                     if (ssh_key_alg(hostkeys[i]) == ssh_key_alg(key)) {
603                         fprintf(stderr, "%s: host key '%s' duplicates key "
604                                 "type %s\n", appname, val,
605                                 ssh_key_alg(key)->ssh_id);
606                         exit(1);
607                     }
608 
609                 sgrowarray(hostkeys, hostkeysize, nhostkeys);
610                 hostkeys[nhostkeys++] = key;
611             } else if (keytype == SSH_KEYTYPE_SSH1) {
612                 if (hostkey1) {
613                     fprintf(stderr, "%s: host key '%s' is a redundant "
614                             "SSH-1 host key\n", appname, val);
615                     exit(1);
616                 }
617                 hostkey1 = snew(RSAKey);
618                 if (!rsa1_load_f(keyfile, hostkey1, NULL, &error)) {
619                     fprintf(stderr, "%s: unable to load host key '%s': "
620                             "%s\n", appname, val, error);
621                     exit(1);
622                 }
623             } else {
624                 fprintf(stderr, "%s: '%s' is not loadable as a "
625                         "private key (%s)", appname, val,
626                         key_type_to_str(keytype));
627                 exit(1);
628             }
629         } else if (longoptarg(arg, "--rsakexkey", &val, &argc, &argv)) {
630             Filename *keyfile;
631             int keytype;
632             const char *error;
633 
634             keyfile = filename_from_str(val);
635             keytype = key_type(keyfile);
636 
637             if (keytype != SSH_KEYTYPE_SSH1) {
638                 fprintf(stderr, "%s: '%s' is not loadable as an SSH-1 format "
639                         "private key (%s)", appname, val,
640                         key_type_to_str(keytype));
641                 exit(1);
642             }
643 
644             if (ssc.rsa_kex_key) {
645                 freersakey(ssc.rsa_kex_key);
646             } else {
647                 ssc.rsa_kex_key = snew(RSAKey);
648             }
649 
650             if (!rsa1_load_f(keyfile, ssc.rsa_kex_key, NULL, &error)) {
651                 fprintf(stderr, "%s: unable to load RSA kex key '%s': "
652                         "%s\n", appname, val, error);
653                 exit(1);
654             }
655 
656             ssc.rsa_kex_key->sshk.vt = &ssh_rsa;
657         } else if (longoptarg(arg, "--userkey", &val, &argc, &argv)) {
658             Filename *keyfile;
659             int keytype;
660             const char *error;
661 
662             keyfile = filename_from_str(val);
663             keytype = key_type(keyfile);
664 
665             if (keytype == SSH_KEYTYPE_SSH2_PUBLIC_RFC4716 ||
666                 keytype == SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH) {
667                 strbuf *sb = strbuf_new();
668                 struct AuthPolicy_ssh2_pubkey *node;
669                 void *blob;
670 
671                 if (!ppk_loadpub_f(keyfile, NULL, BinarySink_UPCAST(sb),
672                                    NULL, &error)) {
673                     fprintf(stderr, "%s: unable to load user key '%s': "
674                             "%s\n", appname, val, error);
675                     exit(1);
676                 }
677 
678                 node = snew_plus(struct AuthPolicy_ssh2_pubkey, sb->len);
679                 blob = snew_plus_get_aux(node);
680                 memcpy(blob, sb->u, sb->len);
681                 node->public_blob = make_ptrlen(blob, sb->len);
682 
683                 node->next = aps.ssh2keys;
684                 aps.ssh2keys = node;
685 
686                 strbuf_free(sb);
687             } else if (keytype == SSH_KEYTYPE_SSH1_PUBLIC) {
688                 strbuf *sb = strbuf_new();
689                 BinarySource src[1];
690                 struct AuthPolicy_ssh1_pubkey *node;
691 
692                 if (!rsa1_loadpub_f(keyfile, BinarySink_UPCAST(sb),
693                                     NULL, &error)) {
694                     fprintf(stderr, "%s: unable to load user key '%s': "
695                             "%s\n", appname, val, error);
696                     exit(1);
697                 }
698 
699                 node = snew(struct AuthPolicy_ssh1_pubkey);
700                 BinarySource_BARE_INIT(src, sb->u, sb->len);
701                 get_rsa_ssh1_pub(src, &node->key, RSA_SSH1_EXPONENT_FIRST);
702 
703                 node->next = aps.ssh1keys;
704                 aps.ssh1keys = node;
705 
706                 strbuf_free(sb);
707             } else {
708                 fprintf(stderr, "%s: '%s' is not loadable as a public key "
709                         "(%s)\n", appname, val, key_type_to_str(keytype));
710                 exit(1);
711             }
712         } else if (longoptarg(arg, "--bannerfile", &val, &argc, &argv)) {
713             FILE *fp = fopen(val, "r");
714             if (!fp) {
715                 fprintf(stderr, "%s: %s: open: %s\n", appname,
716                         val, strerror(errno));
717                 exit(1);
718             }
719             strbuf *sb = strbuf_new();
720             if (!read_file_into(BinarySink_UPCAST(sb), fp)) {
721                 fprintf(stderr, "%s: %s: read: %s\n", appname,
722                         val, strerror(errno));
723                 exit(1);
724             }
725             fclose(fp);
726             ssc.banner = ptrlen_from_strbuf(sb);
727         } else if (longoptarg(arg, "--bannertext", &val, &argc, &argv)) {
728             ssc.banner = ptrlen_from_asciz(val);
729         } else if (longoptarg(arg, "--sessiondir", &val, &argc, &argv)) {
730             ssc.session_starting_dir = val;
731         } else if (longoptarg(arg, "--kexinit-kex", &val, &argc, &argv)) {
732             ssc.kex_override[KEXLIST_KEX] = ptrlen_from_asciz(val);
733         } else if (longoptarg(arg, "--kexinit-hostkey", &val, &argc, &argv)) {
734             ssc.kex_override[KEXLIST_HOSTKEY] = ptrlen_from_asciz(val);
735         } else if (longoptarg(arg, "--kexinit-cscipher", &val, &argc, &argv)) {
736             ssc.kex_override[KEXLIST_CSCIPHER] = ptrlen_from_asciz(val);
737         } else if (longoptarg(arg, "--kexinit-csmac", &val, &argc, &argv)) {
738             ssc.kex_override[KEXLIST_CSMAC] = ptrlen_from_asciz(val);
739         } else if (longoptarg(arg, "--kexinit-cscomp", &val, &argc, &argv)) {
740             ssc.kex_override[KEXLIST_CSCOMP] = ptrlen_from_asciz(val);
741         } else if (longoptarg(arg, "--kexinit-sccipher", &val, &argc, &argv)) {
742             ssc.kex_override[KEXLIST_SCCIPHER] = ptrlen_from_asciz(val);
743         } else if (longoptarg(arg, "--kexinit-scmac", &val, &argc, &argv)) {
744             ssc.kex_override[KEXLIST_SCMAC] = ptrlen_from_asciz(val);
745         } else if (longoptarg(arg, "--kexinit-sccomp", &val, &argc, &argv)) {
746             ssc.kex_override[KEXLIST_SCCOMP] = ptrlen_from_asciz(val);
747         } else if (longoptarg(arg, "--ssh1-ciphers", &val, &argc, &argv)) {
748             ptrlen list = ptrlen_from_asciz(val);
749             ptrlen word;
750             unsigned long mask = 0;
751             while (word = ptrlen_get_word(&list, ","), word.len != 0) {
752 
753 #define SSH1_CIPHER_CASE(bitpos, name)                  \
754                 if (ptrlen_eq_string(word, name)) {     \
755                     mask |= 1U << bitpos;               \
756                     continue;                           \
757                 }
758                 SSH1_SUPPORTED_CIPHER_LIST(SSH1_CIPHER_CASE);
759 #undef SSH1_CIPHER_CASE
760 
761                 fprintf(stderr, "%s: unrecognised SSH-1 cipher '%.*s'\n",
762                         appname, PTRLEN_PRINTF(word));
763                 exit(1);
764             }
765             ssc.ssh1_cipher_mask = mask;
766         } else if (longoptnoarg(arg, "--ssh1-no-compression")) {
767             ssc.ssh1_allow_compression = false;
768         } else if (longoptnoarg(arg, "--exitsignum")) {
769             ssc.exit_signal_numeric = true;
770         } else if (longoptarg(arg, "--sshlog", &val, &argc, &argv) ||
771                    longoptarg(arg, "-sshlog", &val, &argc, &argv)) {
772             Filename *logfile = filename_from_str(val);
773             conf_set_filename(conf, CONF_logfilename, logfile);
774             filename_free(logfile);
775             conf_set_int(conf, CONF_logtype, LGTYP_PACKETS);
776             conf_set_int(conf, CONF_logxfovr, LGXF_OVR);
777         } else if (longoptarg(arg, "--sshrawlog", &val, &argc, &argv) ||
778                    longoptarg(arg, "-sshrawlog", &val, &argc, &argv)) {
779             Filename *logfile = filename_from_str(val);
780             conf_set_filename(conf, CONF_logfilename, logfile);
781             filename_free(logfile);
782             conf_set_int(conf, CONF_logtype, LGTYP_SSHRAW);
783             conf_set_int(conf, CONF_logxfovr, LGXF_OVR);
784         } else if (!strcmp(arg, "--pretend-to-accept-any-pubkey")) {
785             ssc.stunt_pretend_to_accept_any_pubkey = true;
786         } else if (!strcmp(arg, "--open-unconditional-agent-socket")) {
787             ssc.stunt_open_unconditional_agent_socket = true;
788         } else {
789             fprintf(stderr, "%s: unrecognised option '%s'\n", appname, arg);
790             exit(1);
791         }
792     }
793 
794     if (nhostkeys == 0 && !hostkey1) {
795         fprintf(stderr, "%s: specify at least one host key\n", appname);
796         exit(1);
797     }
798 
799     random_ref();
800 
801     /*
802      * Block SIGPIPE, so that we'll get EPIPE individually on
803      * particular network connections that go wrong.
804      */
805     putty_signal(SIGPIPE, SIG_IGN);
806 
807     sk_init();
808     uxsel_init();
809 
810     struct server_config scfg;
811     scfg.conf = conf;
812     scfg.ssc = &ssc;
813     scfg.hostkeys = hostkeys;
814     scfg.nhostkeys = nhostkeys;
815     scfg.hostkey1 = hostkey1;
816     scfg.ap_shared = &aps;
817     scfg.next_id = 0;
818 
819     if (listen_port >= 0 || listen_socket) {
820         listening = true;
821         scfg.listening_plug.vt = &server_plugvt;
822         char *msg;
823         if (listen_port >= 0) {
824             scfg.listening_socket = sk_newlistener(
825                 NULL, listen_port, &scfg.listening_plug, true,
826                 ADDRTYPE_UNSPEC);
827             msg = dupprintf("%s: listening on port %d",
828                             appname, listen_port);
829         } else {
830             SockAddr *addr = unix_sock_addr(listen_socket);
831             scfg.listening_socket = new_unix_listener(
832                 addr, &scfg.listening_plug);
833             msg = dupprintf("%s: listening on Unix socket %s",
834                             appname, listen_socket);
835         }
836 
837         log_to_stderr(-1, msg);
838         sfree(msg);
839     } else {
840         struct server_instance *inst;
841         Plug *plug = server_conn_plug(&scfg, &inst);
842         ssh_server_start(plug, make_fd_socket(0, 1, -1, plug));
843         log_to_stderr(inst->id, "speaking SSH on stdio");
844     }
845 
846     cli_main_loop(cliloop_no_pw_setup, cliloop_no_pw_check,
847                   cliloop_always_continue, NULL);
848 
849     return 0;
850 }
851