From ff43f5d4444485d60f8bb69f1978d462b8b2707d Mon Sep 17 00:00:00 2001 From: Joel Sherrill Date: Sat, 28 Jul 2012 07:10:41 -0500 Subject: telnetd: Added from old stack --- services/telnetd/Makefile | 44 ++ services/telnetd/README | 23 + services/telnetd/check_passwd.c | 107 ++++ services/telnetd/des.c | 879 +++++++++++++++++++++++++++++++ services/telnetd/genpw.c | 79 +++ services/telnetd/include/passwd.h | 25 + services/telnetd/include/rtems/pty.h | 68 +++ services/telnetd/include/rtems/telnetd.h | 110 ++++ services/telnetd/pty.c | 652 +++++++++++++++++++++++ services/telnetd/telnetd.c | 510 ++++++++++++++++++ 10 files changed, 2497 insertions(+) create mode 100644 services/telnetd/Makefile create mode 100644 services/telnetd/README create mode 100644 services/telnetd/check_passwd.c create mode 100644 services/telnetd/des.c create mode 100644 services/telnetd/genpw.c create mode 100644 services/telnetd/include/passwd.h create mode 100644 services/telnetd/include/rtems/pty.h create mode 100644 services/telnetd/include/rtems/telnetd.h create mode 100644 services/telnetd/pty.c create mode 100644 services/telnetd/telnetd.c diff --git a/services/telnetd/Makefile b/services/telnetd/Makefile new file mode 100644 index 00000000..4e6a6e21 --- /dev/null +++ b/services/telnetd/Makefile @@ -0,0 +1,44 @@ +include ../../config.inc + +include $(RTEMS_MAKEFILE_PATH)/Makefile.inc +include $(RTEMS_CUSTOM) +include $(PROJECT_ROOT)/make/leaf.cfg + +CFLAGS += -I $(INSTALL_BASE)/include + +CFLAGS += -w +CFLAGS += -I include +CFLAGS += -std=gnu99 +CFLAGS += -MT $@ -MD -MP -MF $(basename $@).d + +C_FILES = +C_FILES += check_passwd.c +C_FILES += des.c +C_FILES += pty.c +C_FILES += telnetd.c +# genpw.c is native + +C_O_FILES = $(C_FILES:%.c=%.o) +C_D_FILES = $(C_FILES:%.c=%.d) + +LIB = libtelnetd.a + +all: $(LIB) + +$(LIB): $(C_O_FILES) + $(AR) rcu $@ $^ + +install: $(LIB) + install -d $(INSTALL_BASE)/include/rtems + install -c -m 644 include/rtems/pty.h $(INSTALL_BASE)/rtems + install -c -m 644 include/rtems/telnetd.h $(INSTALL_BASE)/rtems + install -c -m 644 include/passwd.h $(INSTALL_BASE)/rtems + install -c -m 644 $(LIB) $(INSTALL_BASE) + +clean: + rm -f $(LIB) $(C_O_FILES) $(C_D_FILES) $(GEN_FILES) + +-include $(C_D_FILES) + +doc: + diff --git a/services/telnetd/README b/services/telnetd/README new file mode 100644 index 00000000..3807aa5e --- /dev/null +++ b/services/telnetd/README @@ -0,0 +1,23 @@ +Author: fernando.ruiz@ctv.es (correo@fernando-ruiz.com) + +This directory contains a telnetd server +primary features: + + + create a user shell pseudo-terminal task. + +This code has not been extensively tested. It is provided as a tool +for RTEMS users to open more shell tcp/ip pseudo-terminal. +Suggestions and comments are appreciated. + +Read libmisc/shell for more information. + +NOTES: + +1. OOB not yet implemented. Only a reduced negotiation is implemented. + +2. If you have tcp/ip initialied you can start telnetd daemon. + You need register pseudo-terminals driver into device drivers table. + +Enjoy it. + +FUTURE: diff --git a/services/telnetd/check_passwd.c b/services/telnetd/check_passwd.c new file mode 100644 index 00000000..5a801b5f --- /dev/null +++ b/services/telnetd/check_passwd.c @@ -0,0 +1,107 @@ +/* + * Authorship + * ---------- + * This software was created by + * Till Straumann , 2003-2007 + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * This software was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + * + * Copyright (c) 2009 embedded brains GmbH and others. + * + * embedded brains GmbH + * Obere Lagerstr. 30 + * D-82178 Puchheim + * Germany + * + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "passwd.h" + +char *__des_crypt_r( const char *, const char *, char *, int); + +/** + * @brief Standard Telnet login check that uses DES to encrypt the passphrase. + * + * Takes a @a passphrase, encrypts it and compares it to the encrypted + * passphrase in the @c TELNETD_PASSWD environment variable. No password is + * required if @c TELNETD_PASSWD is unset. The argument @a user is ignored. + */ +bool rtems_telnetd_login_check( + const char *user, + const char *passphrase +) +{ + char *pw = getenv( "TELNETD_PASSWD"); + char cryptbuf [21]; + char salt [3]; + + if (pw == NULL || strlen( pw) == 0) { + #ifdef TELNETD_DEFAULT_PASSWD + pw = TELNETD_DEFAULT_PASSWD; + #else + return true; + #endif + } + + strncpy( salt, pw, 2); + salt [2] = '\0'; + + return strcmp( + __des_crypt_r( passphrase, salt, cryptbuf, sizeof( cryptbuf)), + pw + ) == 0; +} diff --git a/services/telnetd/des.c b/services/telnetd/des.c new file mode 100644 index 00000000..74e4b8e3 --- /dev/null +++ b/services/telnetd/des.c @@ -0,0 +1,879 @@ +/* + * FreeSec: libcrypt for NetBSD + * + * Copyright (c) 1994 David Burren + * All rights reserved. + * + * Ported to RTEMS and made reentrant by Till Straumann, 9/2003 + * + * Adapted for FreeBSD-2.0 by Geoffrey M. Rehmet + * this file should now *only* export crypt(), in order to make + * binaries of libcrypt exportable from the USA + * + * Adapted for FreeBSD-4.0 by Mark R V Murray + * this file should now *only* export crypt_des(), in order to make + * a module that can be optionally included in libcrypt. + * + * 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. + * 3. Neither the name of the author nor the names of other contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * This is an original implementation of the DES and the crypt(3) interfaces + * by David Burren . + * + * An excellent reference on the underlying algorithm (and related + * algorithms) is: + * + * B. Schneier, Applied Cryptography: protocols, algorithms, + * and source code in C, John Wiley & Sons, 1994. + * + * Note that in that book's description of DES the lookups for the initial, + * pbox, and final permutations are inverted (this has been brought to the + * attention of the author). A list of errata for this book has been + * posted to the sci.crypt newsgroup by the author and is available for FTP. + * + * ARCHITECTURE ASSUMPTIONS: + * It is assumed that the 8-byte arrays passed by reference can be + * addressed as arrays of u_int32_t's (ie. the CPU is not picky about + * alignment). + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define __FORCE_GLIBC +#include +#include +#include +#include +#ifndef __rtems__ +#include +#include +#include +#endif +#include + +#define REENTRANT +/* Re-entrantify me -- all this junk needs to be in + * struct crypt_data to make this really reentrant... */ + +/* TS; not really - only the stuff in Des_Context */ +static struct fixed1 { +u_char inv_key_perm[64]; +u_char inv_comp_perm[56]; +u_char u_sbox[8][64]; +u_char un_pbox[32]; +} des1_f; +static struct fixed2 { +u_int32_t ip_maskl[8][256], ip_maskr[8][256]; +} des2_f; +static struct fixed3 { +u_int32_t fp_maskl[8][256], fp_maskr[8][256]; +} des3_f; +static struct fixed4 { +u_int32_t key_perm_maskl[8][128], key_perm_maskr[8][128]; +u_int32_t comp_maskl[8][128], comp_maskr[8][128]; +} des4_f; + +#define inv_key_perm des1_f.inv_key_perm +#define inv_comp_perm des1_f.inv_comp_perm +#define u_sbox des1_f.u_sbox +#define un_pbox des1_f.un_pbox +#define ip_maskl des2_f.ip_maskl +#define ip_maskr des2_f.ip_maskr +#define fp_maskl des3_f.fp_maskl +#define fp_maskr des3_f.fp_maskr +#define key_perm_maskl des4_f.key_perm_maskl +#define key_perm_maskr des4_f.key_perm_maskr +#define comp_maskl des4_f.comp_maskl +#define comp_maskr des4_f.comp_maskr + +/* These need to be maintained per-process */ +struct Des_Context { +u_int32_t en_keysl[16], en_keysr[16]; +u_int32_t de_keysl[16], de_keysr[16]; +u_int32_t saltbits; +u_int32_t old_salt; +u_int32_t old_rawkey0, old_rawkey1; +}; + +#ifndef REENTRANT +static struct Des_Context single; +#endif + +#define en_keysl des_ctx->en_keysl +#define en_keysr des_ctx->en_keysr +#define de_keysl des_ctx->de_keysl +#define de_keysr des_ctx->de_keysr +#define saltbits des_ctx->saltbits +#define old_salt des_ctx->old_salt +#define old_rawkey0 des_ctx->old_rawkey0 +#define old_rawkey1 des_ctx->old_rawkey1 + +/* Static stuff that stays resident and doesn't change after + * being initialized, and therefore doesn't need to be made + * reentrant. */ +static u_char init_perm[64], final_perm[64]; +static u_char m_sbox[4][4096]; +static u_int32_t psbox[4][256]; + + + + +/* A pile of data */ +static const u_char ascii64[] = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +static const u_char IP[64] = { + 58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, + 62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8, + 57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3, + 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7 +}; + +static const u_char key_perm[56] = { + 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, + 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36, + 63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22, + 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4 +}; + +static const u_char key_shifts[16] = { + 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 +}; + +static const u_char comp_perm[48] = { + 14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, + 23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2, + 41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48, + 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32 +}; + +/* + * No E box is used, as it's replaced by some ANDs, shifts, and ORs. + */ + +static const u_char sbox[8][64] = { + { + 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, + 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, + 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, + 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13 + }, + { + 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, + 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, + 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, + 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9 + }, + { + 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, + 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, + 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, + 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12 + }, + { + 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, + 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, + 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, + 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14 + }, + { + 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, + 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, + 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, + 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3 + }, + { + 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, + 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, + 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, + 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13 + }, + { + 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, + 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, + 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, + 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12 + }, + { + 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, + 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, + 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, + 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11 + } +}; + +static const u_char pbox[32] = { + 16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10, + 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25 +}; + +static const u_int32_t bits32[32] = +{ + 0x80000000, 0x40000000, 0x20000000, 0x10000000, + 0x08000000, 0x04000000, 0x02000000, 0x01000000, + 0x00800000, 0x00400000, 0x00200000, 0x00100000, + 0x00080000, 0x00040000, 0x00020000, 0x00010000, + 0x00008000, 0x00004000, 0x00002000, 0x00001000, + 0x00000800, 0x00000400, 0x00000200, 0x00000100, + 0x00000080, 0x00000040, 0x00000020, 0x00000010, + 0x00000008, 0x00000004, 0x00000002, 0x00000001 +}; + +static const u_char bits8[8] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; +static const u_int32_t *bits28, *bits24; + + +static int +ascii_to_bin(char ch) +{ + if (ch > 'z') + return(0); + if (ch >= 'a') + return(ch - 'a' + 38); + if (ch > 'Z') + return(0); + if (ch >= 'A') + return(ch - 'A' + 12); + if (ch > '9') + return(0); + if (ch >= '.') + return(ch - '.'); + return(0); +} + +struct Des_Context * +des_ctx_init(void) +{ +struct Des_Context *des_ctx; +#ifdef REENTRANT + des_ctx = malloc(sizeof(*des_ctx)); +#else + des_ctx = &single; +#endif + old_rawkey0 = old_rawkey1 = 0L; + saltbits = 0L; + old_salt = 0L; + + return des_ctx; +} + +static void +des_init(void) +{ + int i, j, b, k, inbit, obit; + u_int32_t *p, *il, *ir, *fl, *fr; + static int des_initialised = 0; + + if (des_initialised==1) + return; + +#ifndef REENTRANT + des_ctx_init(); +#endif + + bits24 = (bits28 = bits32 + 4) + 4; + + /* + * Invert the S-boxes, reordering the input bits. + */ + for (i = 0; i < 8; i++) + for (j = 0; j < 64; j++) { + b = (j & 0x20) | ((j & 1) << 4) | ((j >> 1) & 0xf); + u_sbox[i][j] = sbox[i][b]; + } + + /* + * Convert the inverted S-boxes into 4 arrays of 8 bits. + * Each will handle 12 bits of the S-box input. + */ + for (b = 0; b < 4; b++) + for (i = 0; i < 64; i++) + for (j = 0; j < 64; j++) + m_sbox[b][(i << 6) | j] = + (u_char)((u_sbox[(b << 1)][i] << 4) | + u_sbox[(b << 1) + 1][j]); + + /* + * Set up the initial & final permutations into a useful form, and + * initialise the inverted key permutation. + */ + for (i = 0; i < 64; i++) { + init_perm[final_perm[i] = IP[i] - 1] = (u_char)i; + inv_key_perm[i] = 255; + } + + /* + * Invert the key permutation and initialise the inverted key + * compression permutation. + */ + for (i = 0; i < 56; i++) { + inv_key_perm[key_perm[i] - 1] = (u_char)i; + inv_comp_perm[i] = 255; + } + + /* + * Invert the key compression permutation. + */ + for (i = 0; i < 48; i++) { + inv_comp_perm[comp_perm[i] - 1] = (u_char)i; + } + + /* + * Set up the OR-mask arrays for the initial and final permutations, + * and for the key initial and compression permutations. + */ + for (k = 0; k < 8; k++) { + for (i = 0; i < 256; i++) { + *(il = &ip_maskl[k][i]) = 0L; + *(ir = &ip_maskr[k][i]) = 0L; + *(fl = &fp_maskl[k][i]) = 0L; + *(fr = &fp_maskr[k][i]) = 0L; + for (j = 0; j < 8; j++) { + inbit = 8 * k + j; + if (i & bits8[j]) { + if ((obit = init_perm[inbit]) < 32) + *il |= bits32[obit]; + else + *ir |= bits32[obit-32]; + if ((obit = final_perm[inbit]) < 32) + *fl |= bits32[obit]; + else + *fr |= bits32[obit - 32]; + } + } + } + for (i = 0; i < 128; i++) { + *(il = &key_perm_maskl[k][i]) = 0L; + *(ir = &key_perm_maskr[k][i]) = 0L; + for (j = 0; j < 7; j++) { + inbit = 8 * k + j; + if (i & bits8[j + 1]) { + if ((obit = inv_key_perm[inbit]) == 255) + continue; + if (obit < 28) + *il |= bits28[obit]; + else + *ir |= bits28[obit - 28]; + } + } + *(il = &comp_maskl[k][i]) = 0L; + *(ir = &comp_maskr[k][i]) = 0L; + for (j = 0; j < 7; j++) { + inbit = 7 * k + j; + if (i & bits8[j + 1]) { + if ((obit=inv_comp_perm[inbit]) == 255) + continue; + if (obit < 24) + *il |= bits24[obit]; + else + *ir |= bits24[obit - 24]; + } + } + } + } + + /* + * Invert the P-box permutation, and convert into OR-masks for + * handling the output of the S-box arrays setup above. + */ + for (i = 0; i < 32; i++) + un_pbox[pbox[i] - 1] = (u_char)i; + + for (b = 0; b < 4; b++) + for (i = 0; i < 256; i++) { + *(p = &psbox[b][i]) = 0L; + for (j = 0; j < 8; j++) { + if (i & bits8[j]) + *p |= bits32[un_pbox[8 * b + j]]; + } + } + + des_initialised = 1; +} + + +static void +setup_salt(long salt, struct Des_Context *des_ctx) +{ + u_int32_t obit, saltbit; + int i; + + if (salt == old_salt) + return; + old_salt = salt; + + saltbits = 0L; + saltbit = 1; + obit = 0x800000; + for (i = 0; i < 24; i++) { + if (salt & saltbit) + saltbits |= obit; + saltbit <<= 1; + obit >>= 1; + } +} + + +static int +des_setkey(const char *key, struct Des_Context *des_ctx) +{ + u_int32_t k0, k1, rawkey0, rawkey1; + int shifts, round; + + des_init(); + + rawkey0 = ntohl(*(const u_int32_t *) key); + rawkey1 = ntohl(*(const u_int32_t *) (key + 4)); + + if ((rawkey0 | rawkey1) + && rawkey0 == old_rawkey0 + && rawkey1 == old_rawkey1) { + /* + * Already setup for this key. + * This optimisation fails on a zero key (which is weak and + * has bad parity anyway) in order to simplify the starting + * conditions. + */ + return(0); + } + old_rawkey0 = rawkey0; + old_rawkey1 = rawkey1; + + /* + * Do key permutation and split into two 28-bit subkeys. + */ + k0 = key_perm_maskl[0][rawkey0 >> 25] + | key_perm_maskl[1][(rawkey0 >> 17) & 0x7f] + | key_perm_maskl[2][(rawkey0 >> 9) & 0x7f] + | key_perm_maskl[3][(rawkey0 >> 1) & 0x7f] + | key_perm_maskl[4][rawkey1 >> 25] + | key_perm_maskl[5][(rawkey1 >> 17) & 0x7f] + | key_perm_maskl[6][(rawkey1 >> 9) & 0x7f] + | key_perm_maskl[7][(rawkey1 >> 1) & 0x7f]; + k1 = key_perm_maskr[0][rawkey0 >> 25] + | key_perm_maskr[1][(rawkey0 >> 17) & 0x7f] + | key_perm_maskr[2][(rawkey0 >> 9) & 0x7f] + | key_perm_maskr[3][(rawkey0 >> 1) & 0x7f] + | key_perm_maskr[4][rawkey1 >> 25] + | key_perm_maskr[5][(rawkey1 >> 17) & 0x7f] + | key_perm_maskr[6][(rawkey1 >> 9) & 0x7f] + | key_perm_maskr[7][(rawkey1 >> 1) & 0x7f]; + /* + * Rotate subkeys and do compression permutation. + */ + shifts = 0; + for (round = 0; round < 16; round++) { + u_int32_t t0, t1; + + shifts += key_shifts[round]; + + t0 = (k0 << shifts) | (k0 >> (28 - shifts)); + t1 = (k1 << shifts) | (k1 >> (28 - shifts)); + + de_keysl[15 - round] = + en_keysl[round] = comp_maskl[0][(t0 >> 21) & 0x7f] + | comp_maskl[1][(t0 >> 14) & 0x7f] + | comp_maskl[2][(t0 >> 7) & 0x7f] + | comp_maskl[3][t0 & 0x7f] + | comp_maskl[4][(t1 >> 21) & 0x7f] + | comp_maskl[5][(t1 >> 14) & 0x7f] + | comp_maskl[6][(t1 >> 7) & 0x7f] + | comp_maskl[7][t1 & 0x7f]; + + de_keysr[15 - round] = + en_keysr[round] = comp_maskr[0][(t0 >> 21) & 0x7f] + | comp_maskr[1][(t0 >> 14) & 0x7f] + | comp_maskr[2][(t0 >> 7) & 0x7f] + | comp_maskr[3][t0 & 0x7f] + | comp_maskr[4][(t1 >> 21) & 0x7f] + | comp_maskr[5][(t1 >> 14) & 0x7f] + | comp_maskr[6][(t1 >> 7) & 0x7f] + | comp_maskr[7][t1 & 0x7f]; + } + return(0); +} + + +static int +do_des( u_int32_t l_in, u_int32_t r_in, u_int32_t *l_out, u_int32_t *r_out, int count, struct Des_Context *des_ctx) +{ + /* + * l_in, r_in, l_out, and r_out are in pseudo-"big-endian" format. + */ + u_int32_t l, r, *kl, *kr, *kl1, *kr1; + u_int32_t f, r48l, r48r; + int round; + + if (count == 0) { + return(1); + } else if (count > 0) { + /* + * Encrypting + */ + kl1 = en_keysl; + kr1 = en_keysr; + } else { + /* + * Decrypting + */ + count = -count; + kl1 = de_keysl; + kr1 = de_keysr; + } + + /* + * Do initial permutation (IP). + */ + l = ip_maskl[0][l_in >> 24] + | ip_maskl[1][(l_in >> 16) & 0xff] + | ip_maskl[2][(l_in >> 8) & 0xff] + | ip_maskl[3][l_in & 0xff] + | ip_maskl[4][r_in >> 24] + | ip_maskl[5][(r_in >> 16) & 0xff] + | ip_maskl[6][(r_in >> 8) & 0xff] + | ip_maskl[7][r_in & 0xff]; + r = ip_maskr[0][l_in >> 24] + | ip_maskr[1][(l_in >> 16) & 0xff] + | ip_maskr[2][(l_in >> 8) & 0xff] + | ip_maskr[3][l_in & 0xff] + | ip_maskr[4][r_in >> 24] + | ip_maskr[5][(r_in >> 16) & 0xff] + | ip_maskr[6][(r_in >> 8) & 0xff] + | ip_maskr[7][r_in & 0xff]; + + while (count--) { + /* + * Do each round. + */ + kl = kl1; + kr = kr1; + round = 16; + while (round--) { + /* + * Expand R to 48 bits (simulate the E-box). + */ + r48l = ((r & 0x00000001) << 23) + | ((r & 0xf8000000) >> 9) + | ((r & 0x1f800000) >> 11) + | ((r & 0x01f80000) >> 13) + | ((r & 0x001f8000) >> 15); + + r48r = ((r & 0x0001f800) << 7) + | ((r & 0x00001f80) << 5) + | ((r & 0x000001f8) << 3) + | ((r & 0x0000001f) << 1) + | ((r & 0x80000000) >> 31); + /* + * Do salting for crypt() and friends, and + * XOR with the permuted key. + */ + f = (r48l ^ r48r) & saltbits; + r48l ^= f ^ *kl++; + r48r ^= f ^ *kr++; + /* + * Do sbox lookups (which shrink it back to 32 bits) + * and do the pbox permutation at the same time. + */ + f = psbox[0][m_sbox[0][r48l >> 12]] + | psbox[1][m_sbox[1][r48l & 0xfff]] + | psbox[2][m_sbox[2][r48r >> 12]] + | psbox[3][m_sbox[3][r48r & 0xfff]]; + /* + * Now that we've permuted things, complete f(). + */ + f ^= l; + l = r; + r = f; + } + r = l; + l = f; + } + /* + * Do final permutation (inverse of IP). + */ + *l_out = fp_maskl[0][l >> 24] + | fp_maskl[1][(l >> 16) & 0xff] + | fp_maskl[2][(l >> 8) & 0xff] + | fp_maskl[3][l & 0xff] + | fp_maskl[4][r >> 24] + | fp_maskl[5][(r >> 16) & 0xff] + | fp_maskl[6][(r >> 8) & 0xff] + | fp_maskl[7][r & 0xff]; + *r_out = fp_maskr[0][l >> 24] + | fp_maskr[1][(l >> 16) & 0xff] + | fp_maskr[2][(l >> 8) & 0xff] + | fp_maskr[3][l & 0xff] + | fp_maskr[4][r >> 24] + | fp_maskr[5][(r >> 16) & 0xff] + | fp_maskr[6][(r >> 8) & 0xff] + | fp_maskr[7][r & 0xff]; + return(0); +} + + +#if 0 +static int +des_cipher(const char *in, char *out, u_int32_t salt, int count) +{ + u_int32_t l_out, r_out, rawl, rawr; + int retval; + union { + u_int32_t *ui32; + const char *c; + } trans; + + des_init(); + + setup_salt(salt); + + trans.c = in; + rawl = ntohl(*trans.ui32++); + rawr = ntohl(*trans.ui32); + + retval = do_des(rawl, rawr, &l_out, &r_out, count); + + trans.c = out; + *trans.ui32++ = htonl(l_out); + *trans.ui32 = htonl(r_out); + return(retval); +} +#endif + + +#ifndef REENTRANT +void +setkey(const char *key) +{ + int i, j; + u_int32_t packed_keys[2]; + u_char *p; + + p = (u_char *) packed_keys; + + for (i = 0; i < 8; i++) { + p[i] = 0; + for (j = 0; j < 8; j++) + if (*key++ & 1) + p[i] |= bits8[j]; + } + des_setkey(p, &single); +} +#endif + + +#ifndef REENTRANT +void +encrypt(char *block, int flag) +{ + u_int32_t io[2]; + u_char *p; + int i, j; + + des_init(); + + setup_salt(0L, &single); + p = block; + for (i = 0; i < 2; i++) { + io[i] = 0L; + for (j = 0; j < 32; j++) + if (*p++ & 1) + io[i] |= bits32[j]; + } + do_des(io[0], io[1], io, io + 1, flag ? -1 : 1, &single); + for (i = 0; i < 2; i++) + for (j = 0; j < 32; j++) + block[(i << 5) | j] = (io[i] & bits32[j]) ? 1 : 0; +} + +#endif + +char * +__des_crypt_r(const char *key, const char *setting, char *output, int sz) +{ + char *rval = 0; + struct Des_Context *des_ctx; + u_int32_t count, salt, l, r0, r1, keybuf[2]; + u_char *p, *q; + + if (sz < 21) + return NULL; + + des_init(); + des_ctx = des_ctx_init(); + + /* + * Copy the key, shifting each character up by one bit + * and padding with zeros. + */ + q = (u_char *)keybuf; + while (q - (u_char *)keybuf - 8) { + *q++ = *key << 1; + if (*(q - 1)) + key++; + } + if (des_setkey((char *)keybuf, des_ctx)) + goto bailout; + +#if 0 + if (*setting == _PASSWORD_EFMT1) { + int i; + /* + * "new"-style: + * setting - underscore, 4 bytes of count, 4 bytes of salt + * key - unlimited characters + */ + for (i = 1, count = 0L; i < 5; i++) + count |= ascii_to_bin(setting[i]) << ((i - 1) * 6); + + for (i = 5, salt = 0L; i < 9; i++) + salt |= ascii_to_bin(setting[i]) << ((i - 5) * 6); + + while (*key) { + /* + * Encrypt the key with itself. + */ + if (des_cipher((char *)keybuf, (char *)keybuf, 0L, 1)) + goto bailout; + /* + * And XOR with the next 8 characters of the key. + */ + q = (u_char *)keybuf; + while (q - (u_char *)keybuf - 8 && *key) + *q++ ^= *key++ << 1; + + if (des_setkey((char *)keybuf)) + goto bailout; + } + strncpy(output, setting, 9); + + /* + * Double check that we weren't given a short setting. + * If we were, the above code will probably have created + * wierd values for count and salt, but we don't really care. + * Just make sure the output string doesn't have an extra + * NUL in it. + */ + output[9] = '\0'; + p = (u_char *)output + strlen(output); + } else +#endif + { + /* + * "old"-style: + * setting - 2 bytes of salt + * key - up to 8 characters + */ + count = 25; + + salt = (ascii_to_bin(setting[1]) << 6) + | ascii_to_bin(setting[0]); + + output[0] = setting[0]; + /* + * If the encrypted password that the salt was extracted from + * is only 1 character long, the salt will be corrupted. We + * need to ensure that the output string doesn't have an extra + * NUL in it! + */ + output[1] = setting[1] ? setting[1] : output[0]; + + p = (u_char *)output + 2; + } + setup_salt(salt, des_ctx); + /* + * Do it. + */ + if (do_des(0L, 0L, &r0, &r1, (int)count, des_ctx)) + goto bailout; + /* + * Now encode the result... + */ + l = (r0 >> 8); + *p++ = ascii64[(l >> 18) & 0x3f]; + *p++ = ascii64[(l >> 12) & 0x3f]; + *p++ = ascii64[(l >> 6) & 0x3f]; + *p++ = ascii64[l & 0x3f]; + + l = (r0 << 16) | ((r1 >> 16) & 0xffff); + *p++ = ascii64[(l >> 18) & 0x3f]; + *p++ = ascii64[(l >> 12) & 0x3f]; + *p++ = ascii64[(l >> 6) & 0x3f]; + *p++ = ascii64[l & 0x3f]; + + l = r1 << 2; + *p++ = ascii64[(l >> 12) & 0x3f]; + *p++ = ascii64[(l >> 6) & 0x3f]; + *p++ = ascii64[l & 0x3f]; + *p = 0; + + rval = output; +bailout: + free(des_ctx); + return rval; +} + +char * +__des_crypt(const char *key, const char *setting) +{ + static char output[21]; + return __des_crypt_r(key, setting, output, sizeof(output)); +} + + +#ifdef DEBUG + +void +des_snap(void **pf, void **pd) +{ + uint8* pfc; + *pf = malloc(sizeof(struct fixed1) + sizeof(struct fixed2) + sizeof(struct fixed3) + sizeof(struct fixed4)); + pfc = *pf; + memcpy(pfc, &des1_f, sizeof(des1_f)); + pfc += sizeof(des1_f); + memcpy(pfc, &des2_f, sizeof(des2_f)); + pfc += sizeof(des2_f); + memcpy(pfc, &des3_f, sizeof(des3_f)); + pfc += sizeof(des3_f); + memcpy(pfc, &des4_f, sizeof(des4_f)); +// *pd = malloc(sizeof(struct Des_Context)); +// memcpy(*pd, &des_ctx, sizeof(des_ctx)); +} + +void +des_check(void *pf, void *pd) +{ + uint8* pfc1, pfc2, pfc3, pfc4; + pfc1 = pf; + pfc2 = pfc1 + sizeof(des1_f); + pfc3 = pfc2 + sizeof(des2_f); + pfc4 = pfc3 + sizeof(des3_f); + printf("Fixed: do%s differ"/*", Context: do%s differ"*/"\n", + (memcmp(pfc1, &des1_f, sizeof(des1_f)) || + memcmp(pfc2, &des2_f, sizeof(des2_f)) || + memcmp(pfc3, &des4_f, sizeof(des3_f)) || + memcmp(pfc4, &des4_f, sizeof(des4_f))) ? "" : "nt"); +} + +#endif diff --git a/services/telnetd/genpw.c b/services/telnetd/genpw.c new file mode 100644 index 00000000..1cb30bc6 --- /dev/null +++ b/services/telnetd/genpw.c @@ -0,0 +1,79 @@ +/* + * Authorship + * ---------- + * This software was created by + * Till Straumann , 2003-2007 + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * This software was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +static void +usage(char *nm) +{ + fprintf(stderr,"Usage: %s [-h] [-s salt] cleartext_password\n", nm); +} + +int +main(int argc, char **argv) +{ +int ch; +char *salt="td"; + while ( (ch=getopt(argc, argv, "hs:")) >=0 ) { + switch (ch) { + default: fprintf(stderr,"Unknown Option '%c'\n",ch); + case 'h': usage(argv[0]); + return 0; + case 's': salt=optarg; + break; + } + } + if ( optind >= argc ) { + usage(argv[0]); + return 1; + } + printf("#define TELNETD_DEFAULT_PASSWD \"%s\"\n",crypt(argv[optind],salt)); +} diff --git a/services/telnetd/include/passwd.h b/services/telnetd/include/passwd.h new file mode 100644 index 00000000..8d5c299a --- /dev/null +++ b/services/telnetd/include/passwd.h @@ -0,0 +1,25 @@ +/* Define a default password for telnetd here. + * NOTES: + * - this can be overridden at run-time by setting + * the "TELNETD_PASSWD" environment variable. + * As soon as that variable is set, the new password + * is effective - no need to restart telnetd. + * - this must be set to an _encrypted_ password, NOT + * the cleartext. Use the 'genpw' utility to generate + * a password string: + * + * 1) Compile 'genpw.c' for the HOST, i.e. + * cc -o genpw genpw.c -lcrypt + * 1) delete an old password definition from this file. + * 2) run './genpw >> passwd.h'. This will append + * a new definition to this file. + * + * - if no password is defined here, no authentication + * is needed, i.e. telnet is open to the world. + * + * T. Straumann + */ + +/* #undef TELNETD_DEFAULT_PASSWD */ +/* Default password: 'rtems' */ +#define TELNETD_DEFAULT_PASSWD "tduDcyLX12owo" diff --git a/services/telnetd/include/rtems/pty.h b/services/telnetd/include/rtems/pty.h new file mode 100644 index 00000000..1d86294c --- /dev/null +++ b/services/telnetd/include/rtems/pty.h @@ -0,0 +1,68 @@ +/* + * /dev/ptyXX (A first version for pseudo-terminals) + * + * Author: Fernando RUIZ CASAS (fernando.ruiz@ctv.es) + * May 2001 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef _RTEMS_PTY_H +#define _RTEMS_PTY_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* Number of ptys to setup */ +extern size_t rtems_pty_maximum_ptys; + +/* Return the devname for a free pty slot. + * If no slot available (socket>=0) + * then the socket argument is closed + */ +char * rtems_pty_get(int socket); + + +/* OBSOLETE */ +#define get_pty rtems_pty_get + +rtems_device_driver pty_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg); +rtems_device_driver pty_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg); +rtems_device_driver pty_close( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg); +rtems_device_driver pty_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg); +rtems_device_driver pty_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg); +rtems_device_driver pty_control( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg); + + +#define PTY_DRIVER_TABLE_ENTRY \ + { pty_initialize , pty_open , pty_close , \ + pty_read , pty_write , pty_control } + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/services/telnetd/include/rtems/telnetd.h b/services/telnetd/include/rtems/telnetd.h new file mode 100644 index 00000000..a2ebcb7c --- /dev/null +++ b/services/telnetd/include/rtems/telnetd.h @@ -0,0 +1,110 @@ +/* + * Original Author: Fernando RUIZ CASAS (fernando.ruiz@ctv.es) + * May 2001 + * Reworked by Till Straumann and .h overhauled by Joel Sherrill. + * + * Copyright (c) 2009 embedded brains GmbH and others. + * + * embedded brains GmbH + * Obere Lagerstr. 30 + * D-82178 Puchheim + * Germany + * + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + */ + +#ifndef _RTEMS_TELNETD_H +#define _RTEMS_TELNETD_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +bool rtems_telnetd_login_check( + const char *user, + const char *passphrase +); + +/** + * @brief Telnet command type. + */ +typedef void (*rtems_telnetd_command)( + char * /* device name */, + void * /* arg */ +); + +/** + * @brief Telnet configuration structure. + */ +typedef struct { + /** + * @brief Function invoked for each Telnet connection. + * + * The first parameter contains the device name. The second parameter + * contains the argument pointer of this configuration table. + */ + rtems_telnetd_command command; + + /** + * @brief Argument for command function. + */ + void *arg; + + /** + * @brief Task priority. + * + * If this parameter is equal to zero, then the priority of network task is + * used or 100 if this priority is less than two. + */ + rtems_task_priority priority; + + /** + * @brief Task stack size. + */ + size_t stack_size; + + /** + * @brief Login check function. + * + * Method used for login checks. Use @c NULL to disable a login check. + */ + rtems_shell_login_check_t login_check; + + /** + * @brief Keep standard IO of the caller. + * + * Telnet takes over the standard input, output and error associated with + * task, if this parameter is set to @c true. In this case, it will @b not + * listen on any sockets. When this parameter is @c false, Telnet will + * create other tasks for the shell which listen on sockets. + */ + bool keep_stdio; +} rtems_telnetd_config_table; + +/** + * @brief Telnet configuration. + * + * The application must provide this configuration table. It is used by + * rtems_telnetd_initialize() to configure the Telnet subsystem. Do not modify + * the entries after the intialization since it is used internally. + */ +extern rtems_telnetd_config_table rtems_telnetd_config; + +/** + * @brief Initializes the Telnet subsystem. + * + * Uses the application provided @ref rtems_telnetd_config configuration table. + */ +rtems_status_code rtems_telnetd_initialize(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/services/telnetd/pty.c b/services/telnetd/pty.c new file mode 100644 index 00000000..e73f0e51 --- /dev/null +++ b/services/telnetd/pty.c @@ -0,0 +1,652 @@ +/* + * /dev/ptyXX (Support for pseudo-terminals) + * + * Original Author: Fernando RUIZ CASAS (fernando.ruiz@ctv.es) + * May 2001 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Till Straumann + * + * - converted into a loadable module + * - NAWS support / ioctls for querying/setting the window + * size added. + * - don't delete the running task when the connection + * is closed. Rather let 'read()' return a 0 count so + * they may cleanup. Some magic hack works around termios + * limitation. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define DEBUG_WH (1<<0) +#define DEBUG_DETAIL (1<<1) + +/* #define DEBUG DEBUG_WH */ + +/*-----------------------------------------*/ +#include +#include +#include +#include +#include +#include +#include +#include +/*-----------------------------------------*/ +#include +#include +#include +#include +/*-----------------------------------------*/ +#define IAC_ESC 255 +#define IAC_DONT 254 +#define IAC_DO 253 +#define IAC_WONT 252 +#define IAC_WILL 251 +#define IAC_SB 250 +#define IAC_GA 249 +#define IAC_EL 248 +#define IAC_EC 247 +#define IAC_AYT 246 +#define IAC_AO 245 +#define IAC_IP 244 +#define IAC_BRK 243 +#define IAC_DMARK 242 +#define IAC_NOP 241 +#define IAC_SE 240 +#define IAC_EOR 239 + +#define SB_MAX 16 + +extern int rtems_telnetd_maximum_ptys; + +struct pty_tt; +typedef struct pty_tt pty_t; + +struct pty_tt { + char *devname; + struct rtems_termios_tty *ttyp; + tcflag_t c_cflag; + int opened; + int socket; + int last_cr; + unsigned iac_mode; + unsigned char sb_buf[SB_MAX]; + int sb_ind; + int width; + int height; +}; + + +static int telnet_pty_inited=FALSE; +static pty_t *telnet_ptys; + +static rtems_device_major_number pty_major; + + +/* This procedure returns the devname for a pty slot free. + * If not slot availiable (field socket>=0) + * then the socket argument is closed + */ + +char * telnet_get_pty(int socket) +{ + int ndx; + + if (telnet_pty_inited) { +#if 0 + if ( rtems_telnetd_maximum_ptys < 5 ) + rtems_telnetd_maximum_ptys = 5; + + telnet_ptys = malloc( rtems_telnetd_maximum_ptys * sizeof (pty_t) ); +#endif + if ( !telnet_ptys ) { + return NULL; + } + + for (ndx=0;ndxsb_buf[0]) { + case 31: /* NAWS */ + pty->width = (pty->sb_buf[1]<<8) + pty->sb_buf[2]; + pty->height = (pty->sb_buf[3]<<8) + pty->sb_buf[4]; +#if DEBUG & DEBUG_WH + fprintf(stderr, + "Setting width/height to %ix%i\n", + pty->width, + pty->height); +#endif + break; + default: + break; + } + return 0; +} + +static int read_pty(int minor) +{ /* Characters written to the client side*/ + unsigned char value; + unsigned int omod; + int count; + int result; + pty_t *pty=telnet_ptys+minor; + + count=read(pty->socket,&value,sizeof(value)); + if (count<0) + return -1; + + if (count<1) { + /* Unfortunately, there is no way of passing an EOF + * condition through the termios driver. Hence, we + * resort to an ugly hack. Setting cindex>ccount + * causes the termios driver to return a read count + * of '0' which is what we want here. We leave + * 'errno' untouched. + */ + pty->ttyp->cindex=pty->ttyp->ccount+1; + return pty->ttyp->termios.c_cc[VEOF]; + }; + + omod=pty->iac_mode; + pty->iac_mode=0; + switch(omod & 0xff) { + case IAC_ESC: + switch(value) { + case IAC_ESC : + /* in case this is an ESC ESC sequence in SB mode */ + pty->iac_mode = omod>>8; + return IAC_ESC; + case IAC_DONT: + case IAC_DO : + case IAC_WONT: + case IAC_WILL: + pty->iac_mode=value; + return -1; + case IAC_SB : +#if DEBUG & DEBUG_DETAIL + printk("SB\n"); +#endif + pty->iac_mode=value; + pty->sb_ind=0; + return -100; + case IAC_GA : + return -1; + case IAC_EL : + return 0x03; /* Ctrl-C*/ + case IAC_EC : + return '\b'; + case IAC_AYT : + write(pty->socket,IAC_AYT_RSP,strlen(IAC_AYT_RSP)); + return -1; + case IAC_AO : + return -1; + case IAC_IP : + write(pty->socket,IAC_IP_RSP,strlen(IAC_IP_RSP)); + return -1; + case IAC_BRK : + write(pty->socket,IAC_BRK_RSP,strlen(IAC_BRK_RSP)); + return -1; + case IAC_DMARK: + return -2; + case IAC_NOP : + return -1; + case IAC_SE : +#if DEBUG & DEBUG_DETAIL + { + int i; + printk("SE"); + for (i=0; isb_ind; i++) + printk(" %02x",pty->sb_buf[i]); + printk("\n"); + } +#endif + handleSB(pty); + return -101; + case IAC_EOR : + return -102; + default : + return -1; + }; + break; + + case IAC_SB: + pty->iac_mode=omod; + if (IAC_ESC==value) { + pty->iac_mode=(omod<<8)|value; + } else { + if (pty->sb_ind < SB_MAX) + pty->sb_buf[pty->sb_ind++]=value; + } + return -1; + + case IAC_WILL: + if (value==34){ + send_iac(minor,IAC_DONT, 34); /*LINEMODE*/ + send_iac(minor,IAC_DO , 1); /*ECHO */ + } else if (value==31) { + send_iac(minor,IAC_DO , 31); /*NAWS */ +#if DEBUG & DEBUG_DETAIL + printk("replied DO NAWS\n"); +#endif + } else { + send_iac(minor,IAC_DONT,value); + } + return -1; + case IAC_DONT: + return -1; + case IAC_DO : + if (value==3) { + send_iac(minor,IAC_WILL, 3); /* GO AHEAD*/ + } else if (value==1) { + /* ECHO */ + } else { + send_iac(minor,IAC_WONT,value); + }; + return -1; + case IAC_WONT: + if (value==1) { + send_iac(minor,IAC_WILL, 1); + } else { /* ECHO */ + send_iac(minor,IAC_WONT,value); + } + return -1; + default: + if (value==IAC_ESC) { + pty->iac_mode=value; + return -1; + } else { + result=value; + if ( 0 +#if 0 /* pass CRLF through - they should use termios to handle it */ + || ((value=='\n') && (pty->last_cr)) +#endif + /* but map telnet CRNUL to CR down here */ + || ((value==0) && pty->last_cr) + ) result=-1; + pty->last_cr=(value=='\r'); + return result; + }; + }; + /* should never get here but keep compiler happy */ + return -1; +} + +/*-----------------------------------------------------------*/ +static int ptySetAttributes(int minor,const struct termios *t); +static int ptyPollInitialize(int major,int minor,void * arg) ; +static int ptyShutdown(int major,int minor,void * arg) ; +static ssize_t ptyPollWrite(int minor, const char * buf, size_t len) ; +static int ptyPollRead(int minor) ; +static const rtems_termios_callbacks * pty_get_termios_handlers(int polled) ; +/*-----------------------------------------------------------*/ +/* Set the 'Hardware' */ +/*-----------------------------------------------------------*/ +static int +ptySetAttributes(int minor,const struct termios *t) { + if (minorc_cflag; + } else { + return -1; + }; + return 0; +} +/*-----------------------------------------------------------*/ +static int +ptyPollInitialize(int major,int minor,void * arg) { + rtems_libio_open_close_args_t * args = (rtems_libio_open_close_args_t*)arg; + struct termios t; + if (minoriop->data1; + telnet_ptys[minor].iac_mode=0; + telnet_ptys[minor].sb_ind=0; + telnet_ptys[minor].width=0; + telnet_ptys[minor].height=0; + t.c_cflag=B9600|CS8;/* termios default */ + return ptySetAttributes(minor,&t); + } else { + return -1; + } +} +/*-----------------------------------------------------------*/ +static int +ptyShutdown(int major,int minor,void * arg) { + if (minor=0) close(telnet_ptys[minor].socket); + telnet_ptys[minor].socket=-1; + chown(telnet_ptys[minor].devname,2,0); + } else { + return -1; + } + return 0; +} +/*-----------------------------------------------------------*/ +/* Write Characters into pty device */ +/*-----------------------------------------------------------*/ +static ssize_t +ptyPollWrite(int minor, const char * buf, size_t len) { + size_t count; + if (minorbuffer; + pty_t *p = &telnet_ptys[minor]; + + switch (args->command) { + + case TIOCGWINSZ: + + wp->ws_row = p->height; + wp->ws_col = p->width; + args->ioctl_return=0; +#if DEBUG & DEBUG_WH + fprintf(stderr, + "ioctl(TIOCGWINSZ), returning %ix%i\n", + wp->ws_col, + wp->ws_row); +#endif + + return RTEMS_SUCCESSFUL; + + case TIOCSWINSZ: +#if DEBUG & DEBUG_WH + fprintf(stderr, + "ioctl(TIOCGWINSZ), setting %ix%i\n", + wp->ws_col, + wp->ws_row); +#endif + + p->height = wp->ws_row; + p->width = wp->ws_col; + args->ioctl_return=0; + + return RTEMS_SUCCESSFUL; + + default: + + break; + } + + return rtems_termios_ioctl(arg); +} + +static rtems_driver_address_table drvPty = { + my_pty_initialize, + my_pty_open, + my_pty_close, + my_pty_read, + my_pty_write, + my_pty_control +}; + +/*-----------------------------------------------------------*/ +static const rtems_termios_callbacks pty_poll_callbacks = { + ptyPollInitialize, /* FirstOpen */ + ptyShutdown, /* LastClose */ + ptyPollRead, /* PollRead */ + ptyPollWrite, /* Write */ + ptySetAttributes, /* setAttributes */ + NULL, /* stopRemoteTX */ + NULL, /* StartRemoteTX */ + 0 /* outputUsesInterrupts */ +}; +/*-----------------------------------------------------------*/ +static const rtems_termios_callbacks * pty_get_termios_handlers(int polled) { + return &pty_poll_callbacks; +} +/*-----------------------------------------------------------*/ + +static int pty_do_initialize(void) +{ + if ( !telnet_pty_inited ) { + if (RTEMS_SUCCESSFUL==rtems_io_register_driver(0, &drvPty, &pty_major)) + telnet_pty_inited=TRUE; + else + fprintf(stderr,"WARNING: registering the PTY driver FAILED\n"); + } + return telnet_pty_inited; +} + +int telnet_pty_initialize(void) +{ + return pty_do_initialize(); +} + +int telnet_pty_finalize(void) +{ + return pty_do_finalize(); +} diff --git a/services/telnetd/telnetd.c b/services/telnetd/telnetd.c new file mode 100644 index 00000000..622aa142 --- /dev/null +++ b/services/telnetd/telnetd.c @@ -0,0 +1,510 @@ +/***********************************************************/ +/* + * + * The telnet DAEMON + * + * Author: 17,may 2001 + * + * WORK: fernando.ruiz@ctv.es + * HOME: correo@fernando-ruiz.com + * + * After start the net you can start this daemon. + * It uses the previously inited pseudo-terminales (pty.c) + * getting a new terminal with getpty(). This function + * gives a terminal name passing a opened socket like parameter. + * + * With register_telnetd() you add a new command in the shell to start + * this daemon interactively. (Login in /dev/console of course) + * + * Sorry but OOB is not still implemented. (This is the first version) + * + * Till Straumann + * - made the 'shell' interface more generic, i.e. it is now + * possible to have 'telnetd' run an arbitrary 'shell' + * program. + * + * Copyright (c) 2009 embedded brains GmbH and others. + * + * embedded brains GmbH + * Obere Lagerstr. 30 + * D-82178 Puchheim + * Germany + * + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define PARANOIA + +extern char *telnet_get_pty(int socket); +extern int telnet_pty_initialize(void); + +struct shell_args { + char *devname; + void *arg; + char peername[16]; + char delete_myself; +}; + +typedef union uni_sa { + struct sockaddr_in sin; + struct sockaddr sa; +} uni_sa; + +static int sockpeername(int sock, char *buf, int bufsz); + +rtems_id telnetd_dflt_spawn( + const char *name, + unsigned priority, + unsigned stackSize, + void (*fn)(void*), + void *fnarg +); + +/***********************************************************/ +static rtems_id telnetd_task_id = RTEMS_ID_NONE; + +rtems_id (*telnetd_spawn_task)( + const char *, + unsigned, + unsigned, + void (*)(void*), + void * +) = telnetd_dflt_spawn; + +static char *grab_a_Connection( + int des_socket, + uni_sa *srv, + char *peername, + int sz +) +{ + char *rval = 0; +#if 0 + socklen_t size_adr = sizeof(srv->sin); +#else + /* 4.6 doesn't have socklen_t */ + uint32_t size_adr = sizeof(srv->sin); +#endif + int acp_sock; + + acp_sock = accept(des_socket,&srv->sa,&size_adr); + + if (acp_sock<0) { + perror("telnetd:accept"); + goto bailout; + }; + + if ( !(rval=telnet_get_pty(acp_sock)) ) { + syslog( LOG_DAEMON | LOG_ERR, "telnetd: unable to obtain PTY"); + /* NOTE: failing 'do_get_pty()' closed the socket */ + goto bailout; + } + + if (sockpeername(acp_sock, peername, sz)) + strncpy(peername, "", sz); + +#ifdef PARANOIA + syslog(LOG_DAEMON | LOG_INFO, + "telnetd: accepted connection from %s on %s", + peername, + rval); +#endif + +bailout: + + return rval; +} + + +static void release_a_Connection(char *devname, char *peername, FILE **pstd, int n) +{ + +#ifdef PARANOIA + syslog( LOG_DAEMON | LOG_INFO, + "telnetd: releasing connection from %s on %s", + peername, + devname ); +#endif + + while (--n>=0) + if (pstd[n]) fclose(pstd[n]); + +} + +static int sockpeername(int sock, char *buf, int bufsz) +{ + uni_sa peer; + int rval = sock < 0; +#if 0 + socklen_t len = sizeof(peer.sin); +#else + /* 4.6 doesn't have socklen_t */ + uint32_t len = sizeof(peer.sin); +#endif + + if ( !rval ) + rval = getpeername(sock, &peer.sa, &len); + + if ( !rval ) + rval = !inet_ntop( AF_INET, &peer.sin.sin_addr, buf, bufsz ); + + return rval; +} + +static void +spawned_shell(void *arg); + +/***********************************************************/ +static void +rtems_task_telnetd(void *task_argument) +{ + int des_socket; + uni_sa srv; + char *devname; + char peername[16]; + int i=1; + int size_adr; + struct shell_args *arg = NULL; + + if ((des_socket=socket(PF_INET,SOCK_STREAM,0))<0) { + perror("telnetd:socket"); + telnetd_task_id = RTEMS_ID_NONE; + rtems_task_delete(RTEMS_SELF); + }; + setsockopt(des_socket,SOL_SOCKET,SO_KEEPALIVE,&i,sizeof(i)); + + memset(&srv,0,sizeof(srv)); + srv.sin.sin_family=AF_INET; + srv.sin.sin_port=htons(23); + size_adr=sizeof(srv.sin); + if ((bind(des_socket,&srv.sa,size_adr))<0) { + perror("telnetd:bind"); + close(des_socket); + telnetd_task_id = RTEMS_ID_NONE; + rtems_task_delete(RTEMS_SELF); + }; + if ((listen(des_socket,5))<0) { + perror("telnetd:listen"); + close(des_socket); + telnetd_task_id = RTEMS_ID_NONE; + rtems_task_delete(RTEMS_SELF); + }; + + /* we don't redirect stdio as this probably + * was started from the console anyways.. + */ + do { + if (rtems_telnetd_config.keep_stdio) { + bool start = true; + char device_name [32]; + + ttyname_r( 1, device_name, sizeof( device_name)); + if (rtems_telnetd_config.login_check != NULL) { + start = rtems_shell_login_prompt( + stdin, + stderr, + device_name, + rtems_telnetd_config.login_check + ); + } + if (start) { + rtems_telnetd_config.command( device_name, arg->arg); + } else { + syslog( + LOG_AUTHPRIV | LOG_WARNING, + "telnetd: to many wrong passwords entered from %s", + device_name + ); + } + } else { + devname = grab_a_Connection(des_socket, &srv, peername, sizeof(peername)); + + if ( !devname ) { + /* if something went wrong, sleep for some time */ + sleep(10); + continue; + } + + arg = malloc( sizeof(*arg) ); + + arg->devname = devname; + arg->arg = rtems_telnetd_config.arg; + strncpy(arg->peername, peername, sizeof(arg->peername)); + + telnetd_task_id = telnetd_spawn_task( + devname, + rtems_telnetd_config.priority, + rtems_telnetd_config.stack_size, + spawned_shell, + arg + ); + if (telnetd_task_id == RTEMS_ID_NONE) { + FILE *dummy; + + if ( telnetd_spawn_task != telnetd_dflt_spawn ) { + fprintf(stderr,"Telnetd: Unable to spawn child task\n"); + } + + /* hmm - the pty driver slot can only be + * released by opening and subsequently + * closing the PTY - this also closes + * the underlying socket. So we mock up + * a stream... + */ + + if ( !(dummy=fopen(devname,"r+")) ) + perror("Unable to dummy open the pty, losing a slot :-("); + release_a_Connection(devname, peername, &dummy, 1); + free(arg); + sleep(2); /* don't accept connections too fast */ + } + } + } while(1); + + /* TODO: how to free the connection semaphore? But then - + * stopping the daemon is probably only needed during + * development/debugging. + * Finalizer code should collect all the connection semaphore + * counts and eventually clean up... + */ + close(des_socket); + telnetd_task_id = RTEMS_ID_NONE; +} + +rtems_status_code rtems_telnetd_initialize( void) +{ + if (telnetd_task_id != RTEMS_ID_NONE) { + fprintf(stderr, "telnetd already started\n"); + return RTEMS_RESOURCE_IN_USE; + } + + if (rtems_telnetd_config.command == NULL) { + fprintf(stderr, "telnetd setup with invalid command\n"); + return RTEMS_IO_ERROR; + } + + if ( !telnet_pty_initialize() ) { + fprintf(stderr, "telnetd cannot initialize PTY driver\n"); + return RTEMS_IO_ERROR; + } + + /* Check priority */ + if (rtems_telnetd_config.priority <= 0) { + rtems_telnetd_config.priority = rtems_bsdnet_config.network_task_priority; + } + if (rtems_telnetd_config.priority < 2) { + rtems_telnetd_config.priority = 100; + } + + /* Check stack size */ + if (rtems_telnetd_config.stack_size <= 0) { + rtems_telnetd_config.stack_size = (size_t)32 * 1024; + } + + /* Spawn task */ + telnetd_task_id = telnetd_spawn_task( + "TNTD", + rtems_telnetd_config.priority, + rtems_telnetd_config.stack_size, + rtems_task_telnetd, + 0 + ); + if (telnetd_task_id == RTEMS_ID_NONE) { + return RTEMS_IO_ERROR; + } + + /* Print status */ + if (!rtems_telnetd_config.keep_stdio) { + fprintf( + stderr, + "telnetd started with stacksize = %u and priority = %d\n", + (unsigned) rtems_telnetd_config.stack_size, + (unsigned) rtems_telnetd_config.priority + ); + } + + return RTEMS_SUCCESSFUL; +} + +/* utility wrapper */ +static void +spawned_shell(void *targ) +{ + rtems_status_code sc; + FILE *nstd[3]={0}; + FILE *ostd[3]={ stdin, stdout, stderr }; + int i=0; + struct shell_args *arg = targ; + bool login_failed = false; + bool start = true; + + sc=rtems_libio_set_private_env(); + + /* newlib hack/workaround. Before we change stdin/out/err we must make + * sure the internal data are initialized (fileno(stdout) has this sideeffect). + * This should probably be done from RTEMS' libc support layer... + * (T.S., newlibc-1.13; 2005/10) + */ + + fileno(stdout); + + if (RTEMS_SUCCESSFUL != sc) { + rtems_error(sc,"rtems_libio_set_private_env"); + goto cleanup; + } + + /* redirect stdio */ + for (i=0; i<3; i++) { + if ( !(nstd[i]=fopen(arg->devname,"r+")) ) { + perror("unable to open stdio"); + goto cleanup; + } + } + + stdin = nstd[0]; + stdout = nstd[1]; + stderr = nstd[2]; + + #if 0 + printk("STDOUT is now %x (%x) (FD %i/%i)\n", + stdout,nstd[1],fileno(stdout),fileno(nstd[1])); + printf("hello\n"); + write(fileno(stdout),"hellofd\n",8); + #endif + + /* call their routine */ + if (rtems_telnetd_config.login_check != NULL) { + start = rtems_shell_login_prompt( + stdin, + stderr, + arg->devname, + rtems_telnetd_config.login_check + ); + login_failed = !start; + } + if (start) { + rtems_telnetd_config.command( arg->devname, arg->arg); + } + + stdin = ostd[0]; + stdout = ostd[1]; + stderr = ostd[2]; + + if (login_failed) { + syslog( + LOG_AUTHPRIV | LOG_WARNING, + "telnetd: to many wrong passwords entered from %s", + arg->peername + ); + } + +cleanup: + release_a_Connection(arg->devname, arg->peername, nstd, i); + free(arg); +} + +struct wrap_delete_args { + void (*t)(void *); + void *a; +}; + +static rtems_task +wrap_delete(rtems_task_argument arg) +{ + struct wrap_delete_args *pwa = (struct wrap_delete_args *)arg; + register void (*t)(void *) = pwa->t; + register void *a = pwa->a; + + /* free argument before calling function (which may never return if + * they choose to delete themselves) + */ + free(pwa); + t(a); + rtems_task_delete(RTEMS_SELF); +} + +rtems_id +telnetd_dflt_spawn(const char *name, unsigned int priority, unsigned int stackSize, void (*fn)(void *), void* fnarg) +{ + rtems_status_code sc; + rtems_id task_id = RTEMS_ID_NONE; + char nm[4] = {'X','X','X','X' }; + struct wrap_delete_args *pwa = malloc(sizeof(*pwa)); + + strncpy(nm, name, 4); + + if ( !pwa ) { + perror("Telnetd: no memory\n"); + return RTEMS_ID_NONE; + } + + pwa->t = fn; + pwa->a = fnarg; + + if ((sc=rtems_task_create( + rtems_build_name(nm[0], nm[1], nm[2], nm[3]), + (rtems_task_priority)priority, + stackSize, + RTEMS_DEFAULT_MODES, + RTEMS_DEFAULT_ATTRIBUTES | RTEMS_FLOATING_POINT, + &task_id)) || + (sc=rtems_task_start( + task_id, + wrap_delete, + (rtems_task_argument)pwa))) { + free(pwa); + rtems_error(sc,"Telnetd: spawning task failed"); + return RTEMS_ID_NONE; + } + return task_id; +} + +/* convenience routines for CEXP (retrieve stdio descriptors + * from reent structure) + */ +#ifdef stdin +static __inline__ FILE * +_stdin(void) { return stdin; } +#undef stdin +FILE *stdin(void) { return _stdin(); } +#endif +#ifdef stdout +static __inline__ FILE * +_stdout(void) { return stdout; } +#undef stdout +FILE *stdout(void) { return _stdout(); } +#endif +#ifdef stderr +static __inline__ FILE * +_stderr(void) { return stderr; } +#undef stderr +FILE *stderr(void) { return _stderr(); } +#endif + +/* MUST NOT USE stdin & friends below here !!!!!!!!!!!!! */ -- cgit v1.2.3