======================================================================== The SKRONK protocols version 0.6 ======================================================================== Henry Strickland <strick@yak.net> Sun Feb 5 1995 This is a working document, subject to change. Please comment on it! Skronk is a user-level C library that re-implements the usual "posix i/o" (unix man 2) functions and "berkeley socket" functions with a set of functions that can use enhanced or alternate ("skronked") protocols for TCP connections. (Typical enhancements could be authentication and/or encryption of the connection.) A simple negotiation protocol allows the clients and servers to agree on what enhancements are desired or required. Skronk is designed so that your common unix network clients and servers (telnet, sendmail, ftp, nntp, X11, etc.) can be merely relinked with the skronk library (libskronk.a) without changing the source code for the programs. As a matter of configuration and policy, skronked clients and servers may choose either to interoperate with normal (non-skronked) client and servers or to forbid normal connections. In order to not interfere with non-skronked programs, skronked connections take place an alternate TCP server port numbers. /* "Skronk" is a musical term, for a new-york-ish free-jazz * massively saxaphonish kind of music, e.g. John Zorn */ ---- THE UDP PROTOCOL BEGINS HERE ---- THE SKRONK MAP DAEMON A skronk map daemon is a UDP service that tells what skronked services are available from a site, and what alternate TCP server port numbers they use. The skronk map daemon receives "skronk map request" packets and returns "skronk map reply" packets that list pairs of port numbers, mapping normal server port numbers to corresponding skronked port numbers. The skronk map reply packet is sent to the same IP address and host port that the request was received from. If there is no map reply in a certain time, after a couple of map request resends, the skronk map client should assume that skronked services are not currently available on that host. SKRONK MAP REQUEST PACKET See "struct skronk_map_request" below. The comment will say something like "finger skronk@yak.net for info", to explain what is going on to network administrators who see these packets for the first time probing their hosts. SKRONK MAP REPLY PACKET See "struct skronk_map_reply" below. The "serial" field should match the serial field of the map request packet. Replies be cached, using the "ttl" time-to-live field for a timeout. If the request packet cannot be replied to (perhaps because the action was not understood, or the version was wrong), a reply with action SKRONK_MAP_NACK should be returned. If the magic field of the request is wrong, do not reply. (The magic number should be changed if the first five fields are changed; otherwise, version and opcode may be changed to implement new protocols.) Skronk map clients should also recognize as a reply the relevant ICMP packets indicating that there is no skronk map daemon. Version numbers 128 through 255 will not be assigned and are available for experimentation. Action codes 128 through 255 will not be assigned and are available for experimentation. Initial prototypes use UDP port 333 for the skronk map service, until a number is officially allocated. -------------------------------- /* unsigned long = 4 octets, "network" order */ /* unsigned short = 2 octets, "network" order */ /* char = 1 octet, ASCII encoding, NUL terminated */ struct skronk_map_request { unsigned long magic; /* SKRONK_MAGIC */ unsigned long serial; unsigned short version; /* SKRONK_VERSION */ unsigned short action; /* SKRONK_MAP_REQUEST */ unsigned long reserved; /* (corresponds to ttl) */ char comment[1]; /* variable length, NTBS */ /* after NUL, remained of packet ignored */ }; #define SKRONK_MAGIC 0x1F1206FB /* tail(md5("SKRONK_MAGIC\n")) */ #define SKRONK_VERSION 0x0101 /* major 1 minor 1 */ #define SKRONK_MAP_REQUEST 63 /* '?' */ #define SKRONK_MAP_RESPONSE 46 /* '.' */ #define SKRONK_MAP_NACK 33 /* '!' */ #define SKRONK_DEFAULT_TTL 3600 /* one hour */ #define SKRONK_NUM_TRIES 3 /* try three packets */ #define SKRONK_WAIT_TIME 3 /* wait three seconds */ struct skronk_map_reply { unsigned long magic; /* SKRONK_MAGIC */ unsigned long serial; /* matches map request serial */ unsigned short version; /* SKRONK_VERSION */ unsigned short action; /* SKRONK_MAP_REPLY or _NACK */ unsigned long ttl; /* cache time to live, in seconds */ unsigned short map[1]; /* variable length, 0 terminate */ /* after 0, remainder of packet ignored */ }; -------------------------------- EXAMPLE SKRONK MAP REQUEST AND REPLY Map Request Packet: ip header: UDP protocol 128.32.43.52 source_ip_address 199.170.88.5 destination_ip_address udp header: 1066 source_port skronk destination_port (number not assigned yet) body: 0x1F1206FB skronk_magic 2001 skronk_serial 1 version '?' action (SKRONK_MAP_REQUEST) "finger comment (for paranoid net admins) skronk@yak.net for info" Map Request Reply: ip header: UDP protocol 199.170.88.5 source_ip_address 128.32.43.52 destination_ip_address udp header: skronk source_port (number not assigned yet) 1066 destination_port body: 0x1F1206FB skronk_magic 2001 skronk_serial (copied from request) 1 version '.' action (SKRONK_MAP_REPLY) 3600 ttl (one hour) map list: 23, 423, /* skronked TELNET on port 423 */ 25, 425, /* skronked SMTP on port 425 */ 70, 470, /* skronked GOPHER on port 470 */ 80, 480, /* skronked HTTP on port 480 */ 514, 914, /* skronked shell on port 914 */ 750, 350, /* skronked shell on port 350 */ 6000, 6400, /* skronked X11 on port 6400 */ 0 /* zero marks end of list */ /* ... assume other services cannot be skronked */ /* * Notice the skronked ports on this host (199.170.88.5) * have been allocated by the system administrator * using a simple (but arbitrary) rule: * * add 400 to the normal number, unless this has * problems (like it would bring a less-than-1024 * port number to be greater-than-1024), in which * case subtract 400. * * I propose this rule as a default, since it does not seem * to collide with common port numbers, but because we * have and always use the skronk map daemon, each site * could pick different numbers. */ ---- THE UDP PROTOCOL ENDS HERE ---- SKRONK PER-CONNECTION NEGOTIATION /* * The current skronk prototype temporarily works on * a simpler negotiation scheme, but below is the * intended scheme. */ /* * Good questions: * 1. Would telnet-style negotiations be better? * * I conclude not: * -- this offers options to be combined at once * -- this does fewer passes from server to client * -- this uses ASCII names rather than numbers * for options, which allows liberal * experimentation with "x-" names. * * 2. Do we need some escape-character & character-stuffing * to re-open negotiations? * * Maybe... but flow of control probably never returns * to the skronk layers with any such requests, so let's * let some escape-octet (with escape-octet-stuffing) * option for return-to-negotiation be a negotiated * option that can be added later. */ The skronk library does a negotiation at the beginning of each TCP connection, after "connect()" and "accept()" rendezvous, before returning flow of control to the application. This negotiation is transparent to the application, but may be configured with skronk configuration files or environment variables. These negotiations should not be confused with other negotiations specific to the program, such as TELNET negotiations, which would occur later, once control is returned to the application. /* * I should first define 'negotiation line', 'acceptance line', * and 'disconnect' to describe the following... */ When the connection is made, the server writes a line of 10 to 999 octets, composed of ASCII characters, terminating with CRLF, to the client. This line must begin with the eight characters 'S' 'K' 'R' SPACE h t o SPACE where h, t, and o are three decimal digits '0'-'9' (hundreds, tens, and ones places), with leading a zero in the hundreds place if required, specifying the length of this line, counting the CRLF at the end. Thus the minimum length of the line is 10. The reason for putting the length of the line in the front is so that the client can first read 8 characters, and then read the rest of the line, but not try to read any characters beyond that size. After the "SKR hto " follow zero or more words of the regexp form [A-Za-z0-9+][A-Za-z0-9/.+-]* separated by one or more SPACE characters. Case matters. These words specify protocols, and should registered with strick@yak.net, or begin with "x-" for experimental protocols. By sending this line, the server volunteers to server this protocol skronked with any of the listed protocols. The server should list the protocols in order of preference, its favored protocols first. The client reads this line, chooses one (or more) protocols, and responds with a similar line, listing only the protocol(s) that it chooses to use, in a specific "stacking" order, from the first protocol applied to the last. For instance, if it chooses "gzip" compression followed by "des" encryption, it should list them in the order "gzip des". Some protocols choices may not be compatible, and some protocol choices may not have stacking order; this will have to be described when the protocols are defined. If the client does not like any of the choices offered, it may hang up the connection, instead of replying with a line. If the server accepts the protocol the client chose, it responds with 8 characters 'S' 'K' 'R' SPACE 'O' 'K' CR LF If not, it may either disconnect the connection, or it may send another initial negotiation line, which should be different from the initial line offered (probably with fewer options for the client to choose). If the negotiation is accepted, what happens next depends on the accepted protocols, and on the actions of the rest of the program. Typically a selected protocol may have to do some more negotiation or trading of data before control is returned to the application. And later reads and writes from the application to the skronked socket may be intercepted and frobbed. PRIMORDIAL PROTOCOLS For stream encryption: /* needs work */ A simple initial protocol named "dh/idea.1". Use Diffee-Hellman key exchange from RSAREF 2.0. Let the server declare the R_DH_PARAMS, and the size of things. Use IDEA encryption in CFB mode from PGP 2.6. No authentication -- susceptible to man-in-the-middle attacks at connection time. For authentication: /* needs lots of work */ A simple initial protocol named "auth-pgp.1" Use PGP public keys and certificates to create a web of trust and thereby authenticate hosts. Can be combined with "dh/idea.1" or used separately. (This probably requires having secret key pass phrases on multiuser machines, so it's one small step forward.) -------------------------------- END $Header: /x/nepal/x/yak/strick/work/skronk-write/RCS/skronk.proto,v 1.3 95/02/05 01:18:49 strick Exp Locker: strick $