static int dnszones_init(struct dnszones *dnszones, const char *cfgdir, const char *vardir, const char *warning, int (op)()) { int r; long long basefd; byte_zero(dnszones, sizeof (struct dnszones)); /* warning */ dnszones->warning = warning; basefd = open_cwd(); if (basefd == -1) return -1; if (chdir(cfgdir) == -1) { close(basefd); return -1; } r = op(dnszones, cfgdir, vardir, warning); if (fchdir(basefd) == -1) r = -1; close(basefd); return r; }
int main(int argc, char **argv) { char *x; const char *keydir = 0; long long i; struct pollfd p[6]; struct pollfd *q; struct pollfd *watch0; struct pollfd *watch1; struct pollfd *watchtochild; struct pollfd *watchfromchild1; struct pollfd *watchfromchild2; struct pollfd *watchselfpipe; int exitsignal, exitcode; signal(SIGPIPE, SIG_IGN); signal(SIGALRM, timeout); log_init(0, "tinysshd", 0, 0); if (argc < 2) die_usage(USAGE); if (!argv[0]) die_usage(USAGE); for (;;) { if (!argv[1]) break; if (argv[1][0] != '-') break; x = *++argv; if (x[0] == '-' && x[1] == 0) break; if (x[0] == '-' && x[1] == '-' && x[2] == 0) break; while (*++x) { if (*x == 'q') { flagverbose = 0; continue; } if (*x == 'Q') { flagverbose = 1; continue; } if (*x == 'v') { if (flagverbose >= 2) flagverbose = 3; else flagverbose = 2; continue; } if (*x == 'o') { cryptotypeselected |= sshcrypto_TYPEOLDCRYPTO; continue; } if (*x == 'O') { cryptotypeselected &= ~sshcrypto_TYPEOLDCRYPTO; continue; } if (*x == 's') { cryptotypeselected |= sshcrypto_TYPENEWCRYPTO; continue; } if (*x == 'S') { cryptotypeselected &= ~sshcrypto_TYPENEWCRYPTO; continue; } if (*x == 'p') { cryptotypeselected |= sshcrypto_TYPEPQCRYPTO; continue; } if (*x == 'P') { cryptotypeselected &= ~sshcrypto_TYPEPQCRYPTO; continue; } if (*x == 'l') { flaglogger = 1; continue; } if (*x == 'L') { flaglogger = 0; continue; } if (*x == 'x') { if (x[1]) { channel_subsystem_add(x + 1); break; } if (argv[1]) { channel_subsystem_add(*++argv); break; } } die_usage(USAGE); } } keydir = *++argv; if (!keydir) die_usage(USAGE); log_init(flagverbose, "tinysshd", 1, flaglogger); connectioninfo(channel.localip, channel.localport, channel.remoteip, channel.remoteport); log_i4("connection from ", channel.remoteip, ":", channel.remoteport); channel_subsystem_log(); global_init(); blocking_disable(0); blocking_disable(1); blocking_disable(2); /* get server longterm keys */ fdwd = open_cwd(); if (fdwd == -1) die_fatal("unable to open current directory", 0, 0); if (chdir(keydir) == -1) die_fatal("unable to chdir to", keydir, 0); for (i = 0; sshcrypto_keys[i].name; ++i) sshcrypto_keys[i].sign_flagserver |= sshcrypto_kexs[i].cryptotype & cryptotypeselected; for (i = 0; sshcrypto_keys[i].name; ++i) sshcrypto_keys[i].sign_flagclient |= sshcrypto_kexs[i].cryptotype & cryptotypeselected; for (i = 0; sshcrypto_kexs[i].name; ++i) sshcrypto_kexs[i].flagenabled |= sshcrypto_kexs[i].cryptotype & cryptotypeselected; for (i = 0; sshcrypto_ciphers[i].name; ++i) sshcrypto_ciphers[i].flagenabled |= sshcrypto_ciphers[i].cryptotype & cryptotypeselected; /* read public keys */ for (i = 0; sshcrypto_keys[i].name; ++i) { if (!sshcrypto_keys[i].sign_flagserver) continue; if (load(sshcrypto_keys[i].sign_publickeyfilename, sshcrypto_keys[i].sign_publickey, sshcrypto_keys[i].sign_publickeybytes) == -1) { sshcrypto_keys[i].sign_flagserver = 0; if (errno == ENOENT) continue; die_fatal("unable to read public key from file", keydir, sshcrypto_keys[i].sign_publickeyfilename); } } if (fchdir(fdwd) == -1) die_fatal("unable to change directory to working directory", 0, 0); close(fdwd); /* set timeout */ alarm(60); /* send and receive hello */ if (!packet_hello_send()) die_fatal("unable to send hello-string", 0, 0); if (!packet_hello_receive()) die_fatal("unable to receive hello-string", 0, 0); /* send and receive kex */ if (!packet_kex_send()) die_fatal("unable to send kex-message", 0, 0); if (!packet_kex_receive()) die_fatal("unable to receive kex-message", 0, 0); rekeying: /* rekeying */ alarm(60); if (packet.flagrekeying == 1) { buf_purge(&packet.kexrecv); buf_put(&packet.kexrecv, b1.buf, b1.len); if (!packet_kex_send()) die_fatal("unable to send kex-message", 0, 0); } /* send and receive kexdh */ if (!packet_kexdh(keydir, &b1, &b2)) die_fatal("unable to subprocess kexdh", 0, 0); if (packet.flagkeys) log_d1("rekeying: done"); packet.flagkeys = 1; /* note: comunication is encrypted */ /* authentication + authorization */ if (packet.flagauthorized == 0) { if (!packet_auth(&b1, &b2)) die_fatal("authentication failed", 0, 0); packet.flagauthorized = 1; } /* note: user is authenticated and authorized */ alarm(3600); /* main loop */ for (;;) { if (channel_iseof()) if (!packet.sendbuf.len) if (packet.flagchanneleofreceived) break; watch0 = watch1 = 0; watchtochild = watchfromchild1 = watchfromchild2 = 0; watchselfpipe = 0; q = p; if (packet_sendisready()) { watch1 = q; q->fd = 1; q->events = POLLOUT; ++q; } if (packet_recvisready()) { watch0 = q; q->fd = 0; q->events = POLLIN; ++q; } if (channel_writeisready()) { watchtochild = q; q->fd = channel_getfd0(); q->events = POLLOUT; ++q; } if (channel_readisready() && packet_putisready()) { watchfromchild1 = q; q->fd = channel_getfd1(); q->events = POLLIN; ++q; } if (channel_extendedreadisready() && packet_putisready()) { watchfromchild2 = q; q->fd = channel_getfd2(); q->events = POLLIN; ++q; } if (selfpipe[0] != -1) { watchselfpipe = q; q->fd = selfpipe[0]; q->events = POLLIN; ++q; } if (poll(p, q - p, 60000) < 0) { watch0 = watch1 = 0; watchtochild = watchfromchild1 = watchfromchild2 = 0; watchselfpipe = 0; } else { if (watch0) if (!watch0->revents) watch0 = 0; if (watch1) if (!watch1->revents) watch1 = 0; if (watchfromchild1) if (!watchfromchild1->revents) watchfromchild1 = 0; if (watchfromchild2) if (!watchfromchild2->revents) watchfromchild2 = 0; if (watchtochild) if (!watchtochild->revents) watchtochild = 0; if (watchselfpipe) if (!watchselfpipe->revents) watchselfpipe = 0; } if (watchtochild) { /* write data to child */ if (!channel_write()) die_fatal("unable to write data to child", 0, 0); /* try to adjust window */ if (!packet_channel_send_windowadjust(&b1)) die_fatal("unable to send data to network", 0, 0); } /* read data from child */ if (watchfromchild1) packet_channel_send_data(&b2); if (watchfromchild2) packet_channel_send_extendeddata(&b2); /* check child */ if (channel_iseof()) { if (selfpipe[0] == -1) if (open_pipe(selfpipe) == -1) die_fatal("unable to open pipe", 0, 0); signal(SIGCHLD, trigger); if (channel_waitnohang(&exitsignal, &exitcode)) { packet_channel_send_eof(&b2); if (!packet_channel_send_close(&b2, exitsignal, exitcode)) die_fatal("unable to close channel", 0, 0); } } /* send data to network */ if (watch1) if (!packet_send()) die_fatal("unable to send data to network", 0, 0); /* receive data from network */ if (watch0) { alarm(3600); /* refresh timeout */ if (!packet_recv()) { if (channel_iseof()) break; /* XXX */ die_fatal("unable to receive data from network", 0, 0); } } /* process packets */ for (;;) { if (!packet_get(&b1, 0)) { if (!errno) break; die_fatal("unable to get packets from network", 0, 0); } if (b1.len < 1) break; /* XXX */ switch (b1.buf[0]) { case SSH_MSG_CHANNEL_OPEN: if (!packet_channel_open(&b1, &b2)) die_fatal("unable to open channel", 0, 0); break; case SSH_MSG_CHANNEL_REQUEST: if (!packet_channel_request(&b1, &b2)) die_fatal("unable to handle channel-request", 0, 0); break; case SSH_MSG_CHANNEL_DATA: if (!packet_channel_recv_data(&b1)) die_fatal("unable to handle channel-data", 0, 0); break; case SSH_MSG_CHANNEL_EXTENDED_DATA: if (!packet_channel_recv_extendeddata(&b1)) die_fatal("unable to handle channel-extended-data", 0, 0); break; case SSH_MSG_CHANNEL_WINDOW_ADJUST: if (!packet_channel_recv_windowadjust(&b1)) die_fatal("unable to handle channel-window-adjust", 0, 0); break; case SSH_MSG_CHANNEL_EOF: if (!packet_channel_recv_eof(&b1)) die_fatal("unable to handle channel-eof", 0, 0); break; case SSH_MSG_CHANNEL_CLOSE: if (!packet_channel_recv_close(&b1)) die_fatal("unable to handle channel-close", 0, 0); break; case SSH_MSG_KEXINIT: goto rekeying; default: if (!packet_unimplemented(&b1)) die_fatal("unable to send SSH_MSG_UNIMPLEMENTED message", 0, 0); } } } log_i1("finished"); global_die(0); return 111; }
int main(int argc,char **argv) { long long hellopackets; long long r; long long nextaction; signal(SIGPIPE,SIG_IGN); if (!argv[0]) die_usage(0); for (;;) { char *x; if (!argv[1]) break; if (argv[1][0] != '-') break; x = *++argv; if (x[0] == '-' && x[1] == 0) break; if (x[0] == '-' && x[1] == '-' && x[2] == 0) break; while (*++x) { if (*x == 'q') { flagverbose = 0; continue; } if (*x == 'Q') { flagverbose = 1; continue; } if (*x == 'v') { if (flagverbose == 2) flagverbose = 3; else flagverbose = 2; continue; } if (*x == 'c') { if (x[1]) { keydir = x + 1; break; } if (argv[1]) { keydir = *++argv; break; } } die_usage(0); } } if (!nameparse(servername,*++argv)) die_usage("sname must be at most 255 bytes, at most 63 bytes between dots"); if (!hexparse(serverlongtermpk,32,*++argv)) die_usage("pk must be exactly 64 hex characters"); if (!multiipparse(serverip,*++argv)) die_usage("ip must be a comma-separated series of IPv4 addresses"); if (!portparse(serverport,*++argv)) die_usage("port must be an integer between 0 and 65535"); if (!hexparse(serverextension,16,*++argv)) die_usage("ext must be exactly 32 hex characters"); if (!*++argv) die_usage("missing prog"); for (;;) { r = open_read("/dev/null"); if (r == -1) die_fatal("unable to open /dev/null",0,0); if (r > 9) { close(r); break; } } if (keydir) { fdwd = open_cwd(); if (fdwd == -1) die_fatal("unable to open current working directory",0,0); if (chdir(keydir) == -1) die_fatal("unable to change to directory",keydir,0); if (load("publickey",clientlongtermpk,sizeof clientlongtermpk) == -1) die_fatal("unable to read public key from",keydir,0); if (load(".expertsonly/secretkey",clientlongtermsk,sizeof clientlongtermsk) == -1) die_fatal("unable to read secret key from",keydir,0); } else { crypto_box_keypair(clientlongtermpk,clientlongtermsk); } crypto_box_keypair(clientshorttermpk,clientshorttermsk); clientshorttermnonce = randommod(281474976710656LL); crypto_box_beforenm(clientshortserverlong,serverlongtermpk,clientshorttermsk); crypto_box_beforenm(clientlongserverlong,serverlongtermpk,clientlongtermsk); udpfd = socket_udp(); if (udpfd == -1) die_fatal("unable to create socket",0,0); for (hellopackets = 0;hellopackets < NUMIP;++hellopackets) { recent = nanoseconds(); /* send a Hello packet: */ clientextension_init(); clientshorttermnonce_update(); byte_copy(nonce,16,"CurveCP-client-H"); uint64_pack(nonce + 16,clientshorttermnonce); byte_copy(packet,8,"QvnQ5XlH"); byte_copy(packet + 8,16,serverextension); byte_copy(packet + 24,16,clientextension); byte_copy(packet + 40,32,clientshorttermpk); byte_copy(packet + 72,64,allzero); byte_copy(packet + 136,8,nonce + 16); crypto_box_afternm(text,allzero,96,nonce,clientshortserverlong); byte_copy(packet + 144,80,text + 16); socket_send(udpfd,packet,224,serverip + 4 * hellopackets,serverport); nextaction = recent + hellowait[hellopackets] + randommod(hellowait[hellopackets]); for (;;) { long long timeout = nextaction - recent; if (timeout <= 0) break; p[0].fd = udpfd; p[0].events = POLLIN; if (poll(p,1,timeout / 1000000 + 1) < 0) p[0].revents = 0; do { /* try receiving a Cookie packet: */ if (!p[0].revents) break; r = socket_recv(udpfd,packet,sizeof packet,packetip,packetport); if (r != 200) break; if (!(byte_isequal(packetip,4,serverip + 4 * hellopackets) & byte_isequal(packetport,2,serverport) & byte_isequal(packet,8,"RL3aNMXK") & byte_isequal(packet + 8,16,clientextension) & byte_isequal(packet + 24,16,serverextension) )) break; byte_copy(nonce,8,"CurveCPK"); byte_copy(nonce + 8,16,packet + 40); byte_zero(text,16); byte_copy(text + 16,144,packet + 56); if (crypto_box_open_afternm(text,text,160,nonce,clientshortserverlong)) break; byte_copy(servershorttermpk,32,text + 32); byte_copy(servercookie,96,text + 64); byte_copy(serverip,4,serverip + 4 * hellopackets); goto receivedcookie; } while (0); recent = nanoseconds(); } } errno = ETIMEDOUT; die_fatal("no response from server",0,0); receivedcookie: crypto_box_beforenm(clientshortservershort,servershorttermpk,clientshorttermsk); byte_copy(nonce,8,"CurveCPV"); if (keydir) { if (safenonce(nonce + 8,0) == -1) die_fatal("nonce-generation disaster",0,0); } else { randombytes(nonce + 8,16); } byte_zero(text,32); byte_copy(text + 32,32,clientshorttermpk); crypto_box_afternm(text,text,64,nonce,clientlongserverlong); byte_copy(vouch,16,nonce + 8); byte_copy(vouch + 16,48,text + 16); /* server is responding, so start child: */ if (open_pipe(tochild) == -1) die_fatal("unable to create pipe",0,0); if (open_pipe(fromchild) == -1) die_fatal("unable to create pipe",0,0); child = fork(); if (child == -1) die_fatal("unable to fork",0,0); if (child == 0) { if (keydir) if (fchdir(fdwd) == -1) die_fatal("unable to chdir to original directory",0,0); close(8); if (dup(tochild[0]) != 8) die_fatal("unable to dup",0,0); close(9); if (dup(fromchild[1]) != 9) die_fatal("unable to dup",0,0); /* XXX: set up environment variables */ signal(SIGPIPE,SIG_DFL); execvp(*argv,argv); die_fatal("unable to run",*argv,0); } close(fromchild[1]); close(tochild[0]); for (;;) { p[0].fd = udpfd; p[0].events = POLLIN; p[1].fd = fromchild[0]; p[1].events = POLLIN; if (poll(p,2,-1) < 0) { p[0].revents = 0; p[1].revents = 0; } do { /* try receiving a Message packet: */ if (!p[0].revents) break; r = socket_recv(udpfd,packet,sizeof packet,packetip,packetport); if (r < 80) break; if (r > 1152) break; if (r & 15) break; packetnonce = uint64_unpack(packet + 40); if (flagreceivedmessage && packetnonce <= receivednonce) break; if (!(byte_isequal(packetip,4,serverip + 4 * hellopackets) & byte_isequal(packetport,2,serverport) & byte_isequal(packet,8,"RL3aNMXM") & byte_isequal(packet + 8,16,clientextension) & byte_isequal(packet + 24,16,serverextension) )) break; byte_copy(nonce,16,"CurveCP-server-M"); byte_copy(nonce + 16,8,packet + 40); byte_zero(text,16); byte_copy(text + 16,r - 48,packet + 48); if (crypto_box_open_afternm(text,text,r - 32,nonce,clientshortservershort)) break; if (!flagreceivedmessage) { flagreceivedmessage = 1; randombytes(clientlongtermpk,sizeof clientlongtermpk); randombytes(vouch,sizeof vouch); randombytes(servername,sizeof servername); randombytes(servercookie,sizeof servercookie); } receivednonce = packetnonce; text[31] = (r - 64) >> 4; /* child is responsible for reading all data immediately, so we won't block: */ if (writeall(tochild[1],text + 31,r - 63) == -1) goto done; } while (0); do { /* try receiving data from child: */ long long i; if (!p[1].revents) break; r = read(fromchild[0],childbuf,sizeof childbuf); if (r == -1) if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) break; if (r <= 0) goto done; childbuflen = r; for (i = 0;i < childbuflen;++i) { if (childmessagelen < 0) goto done; if (childmessagelen >= sizeof childmessage) goto done; childmessage[childmessagelen++] = childbuf[i]; if (childmessage[0] & 128) goto done; if (childmessagelen == 1 + 16 * (unsigned long long) childmessage[0]) { clientextension_init(); clientshorttermnonce_update(); uint64_pack(nonce + 16,clientshorttermnonce); if (flagreceivedmessage) { r = childmessagelen - 1; if (r < 16) goto done; if (r > 1088) goto done; byte_copy(nonce,16,"CurveCP-client-M"); byte_zero(text,32); byte_copy(text + 32,r,childmessage + 1); crypto_box_afternm(text,text,r + 32,nonce,clientshortservershort); byte_copy(packet,8,"QvnQ5XlM"); byte_copy(packet + 8,16,serverextension); byte_copy(packet + 24,16,clientextension); byte_copy(packet + 40,32,clientshorttermpk); byte_copy(packet + 72,8,nonce + 16); byte_copy(packet + 80,r + 16,text + 16); socket_send(udpfd,packet,r + 96,serverip,serverport); } else { r = childmessagelen - 1; if (r < 16) goto done; if (r > 640) goto done; byte_copy(nonce,16,"CurveCP-client-I"); byte_zero(text,32); byte_copy(text + 32,32,clientlongtermpk); byte_copy(text + 64,64,vouch); byte_copy(text + 128,256,servername); byte_copy(text + 384,r,childmessage + 1); crypto_box_afternm(text,text,r + 384,nonce,clientshortservershort); byte_copy(packet,8,"QvnQ5XlI"); byte_copy(packet + 8,16,serverextension); byte_copy(packet + 24,16,clientextension); byte_copy(packet + 40,32,clientshorttermpk); byte_copy(packet + 72,96,servercookie); byte_copy(packet + 168,8,nonce + 16); byte_copy(packet + 176,r + 368,text + 16); socket_send(udpfd,packet,r + 544,serverip,serverport); } childmessagelen = 0; } } } while (0); } done: do { r = waitpid(child,&childstatus,0); } while (r == -1 && errno == EINTR); if (!WIFEXITED(childstatus)) { errno = 0; die_fatal("process killed by signal",0,0); } return WEXITSTATUS(childstatus); }