summaryrefslogtreecommitdiff
path: root/mactelnet.c
diff options
context:
space:
mode:
Diffstat (limited to 'mactelnet.c')
-rw-r--r--mactelnet.c537
1 files changed, 429 insertions, 108 deletions
diff --git a/mactelnet.c b/mactelnet.c
index f163c5f..935ca52 100644
--- a/mactelnet.c
+++ b/mactelnet.c
@@ -16,162 +16,483 @@
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#include <string.h>
-#include <stdio.h>
#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netinet/ether.h>
+#include <sys/time.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <string.h>
#include <linux/if_ether.h>
-#include "mactelnet.h"
+#include <openssl/md5.h>
+#include "protocol.h"
+#include "udp.h"
+#include "console.h"
+#include "devices.h"
#include "config.h"
-unsigned char mt_mactelnet_cpmagic[4] = { 0x56, 0x34, 0x12, 0xff };
-unsigned char mt_mactelnet_clienttype[2] = { 0x00, 0x15 };
+int sockfd;
+int insockfd;
+int deviceIndex;
+unsigned int outcounter = 0;
+unsigned int incounter = 0;
+int sessionkey = 0;
+int running = 1;
+unsigned char broadcastMode = 1;
+unsigned char terminalMode = 0;
-int initPacket(struct mt_packet *packet, unsigned char ptype, unsigned char *srcmac, unsigned char *dstmac, unsigned short sessionkey, unsigned int counter) {
- unsigned char *data = packet->data;
+unsigned char srcmac[ETH_ALEN];
+unsigned char dstmac[ETH_ALEN];
- /* Packet version */
- data[0] = 1;
+struct in_addr sourceip;
+struct in_addr destip;
+int sourceport;
- /* Packet type */
- data[1] = ptype;
+unsigned char encryptionkey[128];
+unsigned char username[255];
+unsigned char password[255];
- /* src ethernet address */
- memcpy(data + 2, srcmac, ETH_ALEN);
+/* Protocol data direction */
+unsigned char mt_direction_fromserver = 0;
- /* dst ethernet address */
- memcpy(data + 8, dstmac, ETH_ALEN);
+int sendUDP(struct mt_packet *packet) {
- if (mt_direction_fromserver) {
- /* Session key */
- data[16] = sessionkey >> 8;
- data[17] = sessionkey & 0xff;
+ if (broadcastMode) {
+ /* Init SendTo struct */
+ struct sockaddr_in socket_address;
+ socket_address.sin_family = AF_INET;
+ socket_address.sin_port = htons(MT_MACTELNET_PORT);
+ socket_address.sin_addr.s_addr = htonl(INADDR_BROADCAST);
- /* Client type: Mac Telnet */
- memcpy(data + 14, &mt_mactelnet_clienttype, sizeof(mt_mactelnet_clienttype));
+ return sendto(insockfd, packet->data, packet->size, 0, (struct sockaddr*)&socket_address, sizeof(socket_address));
} else {
- /* Session key */
- data[14] = sessionkey >> 8;
- data[15] = sessionkey & 0xff;
+ return sendCustomUDP(sockfd, deviceIndex, srcmac, dstmac, &sourceip, sourceport, &destip, MT_MACTELNET_PORT, packet->data, packet->size);
+ }
- /* Client type: Mac Telnet */
- memcpy(data + 16, &mt_mactelnet_clienttype, sizeof(mt_mactelnet_clienttype));
+}
+
+void sendAuthData(unsigned char *username, unsigned char *password) {
+ struct mt_packet data;
+ unsigned char *terminal = (unsigned char *)getenv("TERM");
+ unsigned short width = 0;
+ unsigned short height = 0;
+ unsigned char md5data[100];
+ unsigned char md5sum[17];
+ int result;
+ int plen;
+ int databytes;
+ MD5_CTX c;
+
+ /* Concat string of 0 + password + encryptionkey */
+ md5data[0] = 0;
+ strncpy(md5data + 1, password, 82);
+ md5data[83] = '\0';
+ memcpy(md5data + 1 + strlen(password), encryptionkey, 16);
+
+ /* Generate md5 sum of md5data with a leading 0 */
+ MD5_Init(&c);
+ MD5_Update(&c, md5data, strlen(password) + 17);
+ MD5_Final(md5sum + 1, &c);
+ md5sum[0] = 0;
+
+ /* Send combined packet to server */
+ plen = initPacket(&data, MT_PTYPE_DATA, srcmac, dstmac, sessionkey, outcounter);
+ databytes = plen;
+ plen += addControlPacket(&data, MT_CPTYPE_PASSWORD, md5sum, 17);
+ plen += addControlPacket(&data, MT_CPTYPE_USERNAME, username, strlen(username));
+ plen += addControlPacket(&data, MT_CPTYPE_TERM_TYPE, terminal, strlen(terminal));
+
+ if (getTerminalSize(&width, &height) != -1) {
+ plen += addControlPacket(&data, MT_CPTYPE_TERM_WIDTH, &width, 2);
+ plen += addControlPacket(&data, MT_CPTYPE_TERM_HEIGHT, &height, 2);
}
- /* Received/sent data counter */
- data[18] = (counter >> 24) & 0xff;
- data[19] = (counter >> 16) & 0xff;
- data[20] = (counter >> 8) & 0xff;
- data[21] = counter & 0xff;
+ outcounter += plen - databytes;
- /* 22 bytes header */
- packet->size = 22;
- return 22;
+ /* TODO: handle result */
+ result = sendUDP(&data);
}
-int addControlPacket(struct mt_packet *packet, char cptype, void *cpdata, int data_len) {
- unsigned char *data = packet->data + packet->size;
+void sig_winch(int sig) {
+ unsigned short width,height;
+ struct mt_packet data;
+ int result,plen,databytes;
- /* Something is really wrong. Packets should never become over 1500 bytes */
- if (packet->size + MT_CPHEADER_LEN + data_len > MT_PACKET_LEN) {
- fprintf(stderr, "addControlPacket: ERROR, too large packet. Exceeds %d bytes\n", MT_PACKET_LEN);
- return -1;
- //exit(1);
- }
+ /* terminal height/width has changed, inform server */
+ if (getTerminalSize(&width, &height) != -1) {
+ plen = initPacket(&data, MT_PTYPE_DATA, srcmac, dstmac, sessionkey, outcounter);
+ databytes = plen;
+ plen += addControlPacket(&data, MT_CPTYPE_TERM_WIDTH, &width, 2);
+ plen += addControlPacket(&data, MT_CPTYPE_TERM_HEIGHT, &height, 2);
+ outcounter += plen - databytes;
- /* PLAINDATA isn't really a controlpacket, but we handle it here, since
- parseControlPacket also parses raw data as PLAINDATA */
- if (cptype == MT_CPTYPE_PLAINDATA) {
- memcpy(data, cpdata, data_len);
- packet->size += data_len;
- return data_len;
+ result = sendUDP(&data);
}
- /* Control Packet Magic id */
- memcpy(data, mt_mactelnet_cpmagic, sizeof(mt_mactelnet_cpmagic));
+ /* reinstate signal handler */
+ signal(SIGWINCH, sig_winch);
+}
- /* Control packet type */
- data[4] = cptype;
+void handlePacket(unsigned char *data, int data_len) {
+ struct mt_mactelnet_hdr pkthdr;
+ parsePacket(data, &pkthdr);
- /* Data length */
- data[5] = (data_len >> 24) & 0xff;
- data[6] = (data_len >> 16) & 0xff;
- data[7] = (data_len >> 8) & 0xff;
- data[8] = data_len & 0xff;
+ /* We only care about packets with correct sessionkey */
+ if (pkthdr.seskey != sessionkey) {
+ return;
+ }
- /* Insert data */
- if (data_len) {
- memcpy(data + MT_CPHEADER_LEN, cpdata, data_len);
+ /* Handle data packets */
+ if (pkthdr.ptype == MT_PTYPE_DATA) {
+ struct mt_packet odata;
+ int plen=0,result=0;
+ int rest = 0;
+ unsigned char *p = data;
+
+ /* Always transmit ACKNOWLEDGE packets in response to DATA packets */
+ plen = initPacket(&odata, MT_PTYPE_ACK, srcmac, dstmac, sessionkey, pkthdr.counter + (data_len - MT_HEADER_LEN));
+ result = sendUDP(&odata);
+
+ /* Accept first packet, and all packets greater than incounter, and if counter has
+ wrapped around. */
+ if (incounter == 0 || pkthdr.counter > incounter || (incounter - pkthdr.counter) > 65535) {
+ incounter = pkthdr.counter;
+ } else {
+ /* Ignore double or old packets */
+ return;
+ }
+
+ /* Calculate how much more there is in the packet */
+ rest = data_len - MT_HEADER_LEN;
+ p += MT_HEADER_LEN;
+
+ while (rest > 0) {
+ int read = 0;
+ struct mt_mactelnet_control_hdr cpkt;
+
+ /* Parse controlpacket data */
+ read = parseControlPacket(p, rest, &cpkt);
+ p += read;
+ rest -= read;
+
+ /* If we receive encryptionkey, transmit auth data back */
+ if (cpkt.cptype == MT_CPTYPE_ENCRYPTIONKEY) {
+ memcpy(encryptionkey, cpkt.data, cpkt.length);
+ sendAuthData(username, password);
+ }
+
+ /* If the (remaining) data did not have a control-packet magic byte sequence,
+ the data is raw terminal data to be outputted to the terminal. */
+ else if (cpkt.cptype == MT_CPTYPE_PLAINDATA) {
+ cpkt.data[cpkt.length] = 0;
+ printf("%s", cpkt.data);
+ }
+
+ /* END_AUTH means that the user/password negotiation is done, and after this point
+ terminal data may arrive, so we set up the terminal to raw mode. */
+ else if (cpkt.cptype == MT_CPTYPE_END_AUTH) {
+ /* stop input buffering at all levels. Give full control of terminal to RouterOS */
+ rawTerm();
+ setvbuf(stdin, (char*)NULL, _IONBF, 0);
+
+ /* we have entered "terminal mode" */
+ terminalMode = 1;
+
+ /* Add resize signal handler */
+ signal(SIGWINCH, sig_winch);
+ }
+ }
+ }
+ else if (pkthdr.ptype == MT_PTYPE_ACK) {
+ /* TODO: If we were resubmitting lost messages, stop resubmitting here if received counter is correct. */
}
- packet->size += MT_CPHEADER_LEN + data_len;
- /* Control packet header length + data length */
- return MT_CPHEADER_LEN + data_len;
+ /* The server wants to terminate the connection, we have to oblige */
+ else if (pkthdr.ptype == MT_PTYPE_END) {
+ struct mt_packet odata;
+ int plen=0,result=0;
+
+ /* Acknowledge the disconnection by sending a END packet in return */
+ plen = initPacket(&odata, MT_PTYPE_END, srcmac, dstmac, pkthdr.seskey, 0);
+ result = sendUDP(&odata);
+
+ fprintf(stderr, "Connection closed.\n");
+
+ /* exit */
+ running = 0;
+ } else {
+ fprintf(stderr, "Unhandeled packet type: %d received from server %s\n", pkthdr.ptype, ether_ntoa((struct ether_addr *)dstmac));
+ }
}
-void parsePacket(unsigned char *data, struct mt_mactelnet_hdr *pkthdr) {
- /* Packet version */
- pkthdr->ver = data[0];
+/*
+ * TODO: Rewrite main() when all sub-functionality is tested
+ */
+int main (int argc, char **argv) {
+ int result;
+ struct mt_packet data;
+ struct sockaddr_in si_me;
+ unsigned char buff[1500];
+ int plen = 0;
+ struct timeval timeout;
+ int keepalive_counter = 0;
+ fd_set read_fds;
+ unsigned char devicename[30];
+ unsigned char printHelp = 0, haveUsername = 0, havePassword = 0;
+ int c;
+
+ while (1) {
+ c = getopt(argc, argv, "nu:p:h?");
+
+ if (c == -1)
+ break;
+
+ switch (c) {
+
+ case 'n':
+ broadcastMode = 0;
+ break;
+
+ case 'u':
+ /* Save username */
+ strncpy(username, optarg, sizeof(username) - 1);
+ username[sizeof(username) - 1] = '\0';
+ haveUsername = 1;
+ break;
+
+ case 'p':
+ /* Save password */
+ strncpy(password, optarg, sizeof(password) - 1);
+ password[sizeof(password) - 1] = '\0';
+ havePassword = 1;
+ break;
+
+ case 'h':
+ case '?':
+ printHelp = 1;
+ break;
+
+ }
+ }
+ if (argc - optind < 2 || printHelp) {
+ fprintf(stderr, "Usage: %s <ifname> <MAC> [-h] [-n] [-u <username>] [-p <password>]\n", argv[0]);
+
+ if (printHelp) {
+ fprintf(stderr, "\nParameters:\n");
+ fprintf(stderr, " ifname Network interface that the RouterOS resides on. (example: eth0)\n");
+ fprintf(stderr, " MAC MAC-Address of the RouterOS device. Use mndp to discover them.\n");
+ fprintf(stderr, " -n Do not use broadcast packets. Less insecure but requires root privileges.\n");
+ fprintf(stderr, " -u Specify username on command line.\n");
+ fprintf(stderr, " -p Specify password on command line.\n");
+ fprintf(stderr, " -h This help.\n");
+ fprintf(stderr, "\n");
+ }
+ return 1;
+ }
- /* Packet type */
- pkthdr->ptype = data[1];
+ /* Save device name */
+ strncpy(devicename, argv[optind++], sizeof(devicename) - 1);
+ devicename[sizeof(devicename) - 1] = '\0';
- /* src ethernet addr */
- memcpy(pkthdr->srcaddr, data+2,6);
+ /* Convert mac address string to ether_addr struct */
+ ether_aton_r(argv[optind], (struct ether_addr *)dstmac);
- /* dst ethernet addr */
- memcpy(pkthdr->dstaddr, data+8,6);
+ /* Seed randomizer */
+ srand(time(NULL));
- if (mt_direction_fromserver) {
- /* Session key */
- pkthdr->seskey = data[14] << 8 | data[15];
+ if (!broadcastMode && geteuid() != 0) {
+ fprintf(stderr, "You need to have root privileges to use the -n parameter.\n");
+ return 1;
+ }
- /* server type */
- memcpy(&(pkthdr->clienttype), data+16, 2);
- } else {
- /* server type */
- memcpy(&(pkthdr->clienttype), data+14, 2);
+ if (!broadcastMode) {
+ /* Transmit raw packets with this socket */
+ sockfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+ if (sockfd < 0) {
+ perror("sockfd");
+ return 1;
+ }
+ }
- /* Session key */
- pkthdr->seskey = data[16] << 8 | data[17];
+ /* Receive regular udp packets with this socket */
+ insockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (insockfd < 0) {
+ perror("insockfd");
+ return 1;
}
- /* Received/sent data counter */
- pkthdr->counter = data[18] << 24 | data[19] << 16 | data[20] << 8 | data[21];
+ if (broadcastMode) {
+ int optval = 1;
+ if (setsockopt(insockfd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof (optval))==-1) {
+ perror("SO_BROADCAST");
+ return 1;
+ }
+ }
- /* Set pointer to actual data */
- pkthdr->data = data + 22;
-}
+ /* Find device index number for specified interface */
+ deviceIndex = getDeviceIndex(insockfd, devicename);
+ if (deviceIndex < 0) {
+ fprintf(stderr, "Device %s not found.\n", devicename);
+ return 1;
+ }
+ /*
+ * We want to show who we are (ip), even though the server only cares
+ * about it's own MAC address in the headers.
+ */
+ result = getDeviceIp(insockfd, devicename, &si_me);
+ if (result < 0) {
+ fprintf(stderr, "Cannot determine IP of device %s\n", devicename);
+ return 1;
+ }
-int parseControlPacket(unsigned char *data, const int data_len, struct mt_mactelnet_control_hdr *cpkthdr) {
+ /* Determine source mac address */
+ result = getDeviceMAC(insockfd, devicename, srcmac);
+ if (result < 0) {
+ fprintf(stderr, "Cannot determine MAC address of device %s\n", devicename);
+ return 1;
+ }
- if (data_len < 0)
- return 0;
+ if (!haveUsername) {
+ int ret=0;
+ printf("Login: ");
+ scanf("%254s", username);
+ }
- /* Check for valid minimum packet length & magic header */
- if (data_len >= 9 && memcmp(data, &mt_mactelnet_cpmagic, 4) == 0) {
+ if (!havePassword) {
+ char *tmp;
+ tmp = getpass("Passsword: ");
+ strncpy(password, tmp, sizeof(password) - 1);
+ password[sizeof(password) - 1] = '\0';
+ /* security */
+ memset(tmp, 0, strlen(tmp));
+#ifdef __GNUC__
+ free(tmp);
+#endif
+ }
- /* Control packet type */
- cpkthdr->cptype = data[4];
- /* Control packet data length */
- cpkthdr->length = data[5] << 24 | data[6] << 16 | data[7] << 8 | data[8];
+ /* Set random source port */
+ sourceport = 1024 + (rand() % 1024);
- /* Set pointer to actual data */
- cpkthdr->data = data + 9;
+ /* Set up global info about the connection */
+ inet_pton(AF_INET, (char *)"255.255.255.255", &destip);
+ memcpy(&sourceip, &(si_me.sin_addr), 4);
- /* Return number of bytes in packet */
- return cpkthdr->length + 9;
+ /* Initialize receiving socket on the device chosen */
+ memset((char *) &si_me, 0, sizeof(si_me));
+ si_me.sin_family = AF_INET;
+ si_me.sin_port = htons(sourceport);
- } else {
- /* Mark data as raw terminal data */
- cpkthdr->cptype = MT_CPTYPE_PLAINDATA;
- cpkthdr->length = data_len;
- cpkthdr->data = data;
+ /* Bind to udp port */
+ if (bind(insockfd, (struct sockaddr *)&si_me, sizeof(si_me)) == -1) {
+ fprintf(stderr, "Error binding to %s:%d, %s\n", inet_ntoa(si_me.sin_addr), sourceport, strerror(errno));
+ return 1;
+ }
+
+ /* Sessioon key */
+ sessionkey = rand() % 65535;
+
+ /* stop output buffering */
+ setvbuf(stdout, (char*)NULL, _IONBF, 0);
- /* Consume the whole rest of the packet */
- return data_len;
+ printf("Connecting to %s...", ether_ntoa((struct ether_addr *)dstmac));
+
+ plen = initPacket(&data, MT_PTYPE_SESSIONSTART, srcmac, dstmac, sessionkey, 0);
+ result = sendUDP(&data);
+
+ /* Try to connect with a timeout */
+ FD_ZERO(&read_fds);
+ FD_SET(insockfd, &read_fds);
+ timeout.tv_sec = 5;
+ timeout.tv_usec = 0;
+ select(insockfd+1, &read_fds, NULL, NULL, &timeout);
+
+ if (!FD_ISSET(insockfd, &read_fds)) {
+ fprintf(stderr, "Connection timed out\n");
+ exit(1);
}
-}
+ result = recvfrom(insockfd, buff, 1400, 0, 0, 0);
+ if (result < 1) {
+ fprintf(stderr, "Connection failed.\n");
+ return 1;
+ }
+ printf("done\n");
+
+ /* Handle first received packet */
+ handlePacket(buff, result);
+
+ /*
+ * TODO: Should resubmit whenever a PTYPE_DATA packet is sent, and an ACK packet with correct datacounter is received
+ * or time out the connection, in all other cases.
+ */
+ plen = initPacket(&data, MT_PTYPE_DATA, srcmac, dstmac, sessionkey, 0);
+ plen = addControlPacket(&data, MT_CPTYPE_BEGINAUTH, NULL, 0);
+ outcounter += plen;
+
+ /* TODO: handle result of sendUDP */
+ result = sendUDP(&data);
+
+ while (running) {
+ int reads;
+
+ /* Init select */
+ FD_ZERO(&read_fds);
+ FD_SET(0, &read_fds);
+ FD_SET(insockfd, &read_fds);
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+
+ /* Wait for data or timeout */
+ reads = select(insockfd+1, &read_fds, NULL, NULL, &timeout);
+ if (reads > 0) {
+ /* Handle data from server */
+ if (FD_ISSET(insockfd, &read_fds)) {
+ memset(buff, 0, 1500);
+ result = recvfrom(insockfd, buff, 1500, 0, 0, 0);
+ handlePacket(buff, result);
+ }
+ /* Handle data from keyboard/local terminal */
+ if (FD_ISSET(0, &read_fds)) {
+ unsigned char keydata[100];
+ int datalen;
+
+ datalen = read(STDIN_FILENO, &keydata, 100);
+
+ plen = initPacket(&data, MT_PTYPE_DATA, srcmac, dstmac, sessionkey, outcounter);
+ plen += addControlPacket(&data, MT_CPTYPE_PLAINDATA, &keydata, datalen);
+ outcounter += datalen;
+ result = sendUDP(&data);
+ }
+ /* Handle select() timeout */
+ } else {
+ /* handle keepalive counter, transmit keepalive packet every 10 seconds
+ of inactivity */
+ if ((keepalive_counter++ % 10) == 0) {
+ struct mt_packet odata;
+ int plen=0,result=0;
+ plen = initPacket(&odata, MT_PTYPE_ACK, srcmac, dstmac, sessionkey, 0);
+ result = sendUDP(&odata);
+ }
+ }
+ }
+
+ if (terminalMode) {
+ /* Reset terminal back to old settings */
+ resetTerm();
+ }
+
+ close(sockfd);
+ close(insockfd);
+
+ return 0;
+}