source: rtems/cpukit/telnetd/telnetd.c @ 8a775c27

4.104.11
Last change on this file since 8a775c27 was 8a775c27, checked in by Joel Sherrill <joel.sherrill@…>, on Mar 27, 2009 at 1:45:31 PM

2009-03-27 Sebastian Huber <sebastian.huber@…>

  • Makefile.am, preinstall.am, libmisc/Makefile.am, libmisc/shell/shell.c, libmisc/shell/shell.h, telnetd/check_passwd.c, telnetd/telnetd.c, telnetd/telnetd.h: Generalized login check.
  • libmisc/shell/login.h, libmisc/shell/login_check.c, libmisc/shell/login_prompt.c: New files.
  • libmisc/stackchk/check.c: Changed format for blown stack message.
  • libcsupport/src/libio_sockets.c: Removed superfluous cast.
  • libnetworking/rtems/ftpfs.h: Documentation.
  • Property mode set to 100644
File size: 12.2 KB
Line 
1/***********************************************************/
2/*
3 *
4 *  The telnet DAEMON
5 *
6 *  Author: 17,may 2001
7 *
8 *   WORK: fernando.ruiz@ctv.es
9 *   HOME: correo@fernando-ruiz.com
10 *
11 * After start the net you can start this daemon.
12 * It uses the previously inited pseudo-terminales (pty.c)
13 * getting a new terminal with getpty(). This function
14 * gives a terminal name passing a opened socket like parameter.
15 *
16 * With register_telnetd() you add a new command in the shell to start
17 * this daemon interactively. (Login in /dev/console of course)
18 *
19 * Sorry but OOB is not still implemented. (This is the first version)
20 *
21 * Till Straumann <strauman@slac.stanford.edu>
22 *  - made the 'shell' interface more generic, i.e. it is now
23 *    possible to have 'telnetd' run an arbitrary 'shell'
24 *    program.
25 *
26 * Copyright (c) 2009
27 * embedded brains GmbH
28 * Obere Lagerstr. 30
29 * D-82178 Puchheim
30 * Germany
31 * <rtems@embedded-brains.de>
32 *
33 * Modified by Sebastian Huber <sebastian.huber@embedded-brains.de>.
34 *
35 * The license and distribution terms for this file may be
36 * found in the file LICENSE in this distribution or at
37 * http://www.rtems.com/license/LICENSE.
38 *
39 * $Id$
40 */
41
42#ifdef HAVE_CONFIG_H
43#include "config.h"
44#endif
45
46#include <rtems.h>
47#include <rtems/error.h>
48#include <rtems/pty.h>
49#include <rtems/shell.h>
50#include <rtems/telnetd.h>
51#include <rtems/bspIo.h>
52#include <sys/socket.h>
53#include <netinet/in.h>
54#include <arpa/inet.h>
55#include <unistd.h>
56#include <stdlib.h>
57#include <stdio.h>
58#include <assert.h>
59#include <string.h>
60#include <syslog.h>
61
62#include <rtems/userenv.h>
63#include <rtems/error.h>
64#include <rtems/rtems_bsdnet.h>
65
66#define PARANOIA
67
68extern char *telnet_get_pty(int socket);
69extern int   telnet_pty_initialize(void);
70
71struct shell_args {
72  char *devname;
73  void *arg;
74  char  peername[16];
75  char  delete_myself;
76};
77
78typedef union uni_sa {
79  struct sockaddr_in sin;
80  struct sockaddr     sa;
81} uni_sa;
82
83static int sockpeername(int sock, char *buf, int bufsz);
84
85void *telnetd_dflt_spawn(
86  const char *name,
87  unsigned priority,
88  unsigned stackSize,
89  void (*fn)(void*),
90  void *fnarg
91);
92
93/***********************************************************/
94static rtems_id telnetd_task_id = RTEMS_ID_NONE;
95
96void *(*telnetd_spawn_task)(
97  const char *,
98  unsigned,
99  unsigned,
100  void (*)(void*),
101  void *
102) = telnetd_dflt_spawn;
103
104static char *grab_a_Connection(
105  int des_socket,
106  uni_sa *srv,
107  char *peername,
108  int sz
109)
110{
111  char *rval = 0;
112#if 0
113  socklen_t size_adr = sizeof(srv->sin);
114#else
115  /* 4.6 doesn't have socklen_t */
116  uint32_t size_adr = sizeof(srv->sin);
117#endif
118  int acp_sock;
119
120  acp_sock = accept(des_socket,&srv->sa,&size_adr);
121
122  if (acp_sock<0) {
123    perror("telnetd:accept");
124    goto bailout;
125  };
126
127  if ( !(rval=telnet_get_pty(acp_sock)) ) {
128    syslog( LOG_DAEMON | LOG_ERR, "telnetd: unable to obtain PTY");
129    /* NOTE: failing 'do_get_pty()' closed the socket */
130    goto bailout;
131  }
132
133  if (sockpeername(acp_sock, peername, sz))
134    strncpy(peername, "<UNKNOWN>", sz);
135
136#ifdef PARANOIA
137  syslog(LOG_DAEMON | LOG_INFO,
138      "telnetd: accepted connection from %s on %s",
139      peername,
140      rval);
141#endif
142
143bailout:
144
145  return rval;
146}
147
148
149static void release_a_Connection(char *devname, char *peername, FILE **pstd, int n)
150{
151
152#ifdef PARANOIA
153  syslog( LOG_DAEMON | LOG_INFO,
154      "telnetd: releasing connection from %s on %s",
155      peername,
156      devname );
157#endif
158
159  while (--n>=0)
160    if (pstd[n]) fclose(pstd[n]);
161
162}
163
164static int sockpeername(int sock, char *buf, int bufsz)
165{
166  uni_sa peer;
167  int    rval = sock < 0;
168#if 0
169  socklen_t len  = sizeof(peer.sin);
170#else
171  /* 4.6 doesn't have socklen_t */
172  uint32_t len  = sizeof(peer.sin);
173#endif
174
175  if ( !rval )
176    rval = getpeername(sock, &peer.sa, &len);
177
178  if ( !rval )
179    rval = !inet_ntop( AF_INET, &peer.sin.sin_addr, buf, bufsz );
180
181  return rval;
182}
183
184static void
185spawned_shell(void *arg);
186
187/***********************************************************/
188static void
189rtems_task_telnetd(void *task_argument)
190{
191  int                des_socket;
192  uni_sa             srv;
193  char              *devname;
194  char               peername[16];
195  int                i=1;
196  int                size_adr;
197  struct shell_args *arg = NULL;
198
199  if ((des_socket=socket(PF_INET,SOCK_STREAM,0))<0) {
200    perror("telnetd:socket");
201    telnetd_task_id = RTEMS_ID_NONE;
202    rtems_task_delete(RTEMS_SELF);
203  };
204  setsockopt(des_socket,SOL_SOCKET,SO_KEEPALIVE,&i,sizeof(i));
205
206  memset(&srv,0,sizeof(srv));
207  srv.sin.sin_family=AF_INET;
208  srv.sin.sin_port=htons(23);
209  size_adr=sizeof(srv.sin);
210  if ((bind(des_socket,&srv.sa,size_adr))<0) {
211    perror("telnetd:bind");
212    close(des_socket);
213    telnetd_task_id = RTEMS_ID_NONE;
214    rtems_task_delete(RTEMS_SELF);
215  };
216  if ((listen(des_socket,5))<0) {
217    perror("telnetd:listen");
218          close(des_socket);
219    telnetd_task_id = RTEMS_ID_NONE;
220    rtems_task_delete(RTEMS_SELF);
221  };
222
223  /* we don't redirect stdio as this probably
224   * was started from the console anyways..
225   */
226  do {
227    if (rtems_telnetd_config.keep_stdio) {
228      bool start = true;
229      char device_name [32];
230
231      ttyname_r( 1, device_name, sizeof( device_name));
232      if (rtems_telnetd_config.login_check != NULL) {
233        start = rtems_shell_login_prompt(
234          stdin,
235          stderr,
236          device_name,
237          rtems_telnetd_config.login_check
238        );
239      }
240      if (start) {
241        rtems_telnetd_config.command( device_name, arg->arg);
242      } else {
243        syslog(
244          LOG_AUTHPRIV | LOG_WARNING,
245          "telnetd: to many wrong passwords entered from %s",
246          device_name
247        );
248      }
249    } else {
250      devname = grab_a_Connection(des_socket, &srv, peername, sizeof(peername));
251
252      if ( !devname ) {
253        /* if something went wrong, sleep for some time */
254        sleep(10);
255        continue;
256      }
257
258      arg = malloc( sizeof(*arg) );
259
260      arg->devname = devname;
261      arg->arg = rtems_telnetd_config.arg;
262      strncpy(arg->peername, peername, sizeof(arg->peername));
263
264      telnetd_task_id = (rtems_id) telnetd_spawn_task(
265        devname,
266        rtems_telnetd_config.priority,
267        rtems_telnetd_config.stack_size,
268        spawned_shell,
269        arg
270      );
271      if (telnetd_task_id == RTEMS_ID_NONE) {
272        FILE *dummy;
273
274        if ( telnetd_spawn_task != telnetd_dflt_spawn ) {
275          fprintf(stderr,"Telnetd: Unable to spawn child task\n");
276        }
277
278        /* hmm - the pty driver slot can only be
279         * released by opening and subsequently
280         * closing the PTY - this also closes
281         * the underlying socket. So we mock up
282         * a stream...
283         */
284
285        if ( !(dummy=fopen(devname,"r+")) )
286          perror("Unable to dummy open the pty, losing a slot :-(");
287        release_a_Connection(devname, peername, &dummy, 1);
288        free(arg);
289        sleep(2); /* don't accept connections too fast */
290      }
291    }
292  } while(1);
293
294  /* TODO: how to free the connection semaphore? But then -
295   *       stopping the daemon is probably only needed during
296   *       development/debugging.
297   *       Finalizer code should collect all the connection semaphore
298   *       counts and eventually clean up...
299   */
300  close(des_socket);
301  telnetd_task_id = RTEMS_ID_NONE;
302}
303
304rtems_status_code rtems_telnetd_initialize( void)
305{
306  rtems_status_code sc = RTEMS_SUCCESSFUL;
307
308  if (telnetd_task_id != RTEMS_ID_NONE) {
309    fprintf(stderr, "telnetd already started\n");
310    return RTEMS_RESOURCE_IN_USE;
311  }
312
313  if (rtems_telnetd_config.command == NULL) {
314    fprintf(stderr, "telnetd setup with invalid command\n");
315    return RTEMS_IO_ERROR;
316  }
317
318  if ( !telnet_pty_initialize() ) {
319    fprintf(stderr, "telnetd cannot initialize PTY driver\n");
320    return RTEMS_IO_ERROR;
321  }
322
323  /* Check priority */
324  if (rtems_telnetd_config.priority <= 0) {
325    rtems_telnetd_config.priority = rtems_bsdnet_config.network_task_priority;
326  }
327  if (rtems_telnetd_config.priority < 2) {
328    rtems_telnetd_config.priority = 100;
329  }
330
331  /* Check stack size */
332  if (rtems_telnetd_config.stack_size <= 0) {
333    rtems_telnetd_config.stack_size = 32 * 1024;
334  }
335
336  /* Spawn task */
337  telnetd_task_id = (rtems_id) telnetd_spawn_task(
338    "TNTD",
339    rtems_telnetd_config.priority,
340    RTEMS_MINIMUM_STACK_SIZE,
341    rtems_task_telnetd,
342    0
343  );
344  if (telnetd_task_id == RTEMS_ID_NONE) {
345    return RTEMS_IO_ERROR;
346  }
347
348  /* Print status */
349  if (!rtems_telnetd_config.keep_stdio) {
350    fprintf(
351      stderr,
352      "telnetd started with stacksize = %u and priority = %d\n",
353      (unsigned) rtems_telnetd_config.stack_size,
354      (unsigned) rtems_telnetd_config.priority
355    );
356  }
357
358  return RTEMS_SUCCESSFUL;
359}
360
361/* utility wrapper */
362static void
363spawned_shell(void *targ)
364{
365  rtems_status_code    sc;
366  FILE                *nstd[3]={0};
367  FILE                *ostd[3]={ stdin, stdout, stderr };
368  int                  i=0;
369  struct shell_args  *arg = targ;
370  bool login_failed = false;
371  bool start = true;
372
373  sc=rtems_libio_set_private_env();
374
375  /* newlib hack/workaround. Before we change stdin/out/err we must make
376         * sure the internal data are initialized (fileno(stdout) has this sideeffect).
377   * This should probably be done from RTEMS' libc support layer...
378   * (T.S., newlibc-1.13; 2005/10)
379         */
380
381  fileno(stdout);
382
383  if (RTEMS_SUCCESSFUL != sc) {
384    rtems_error(sc,"rtems_libio_set_private_env");
385    goto cleanup;
386  }
387
388  /* redirect stdio */
389  for (i=0; i<3; i++) {
390    if ( !(nstd[i]=fopen(arg->devname,"r+")) ) {
391      perror("unable to open stdio");
392      goto cleanup;
393    }
394  }
395
396  stdin  = nstd[0];
397  stdout = nstd[1];
398  stderr = nstd[2];
399
400  #if 0
401    printk("STDOUT is now %x (%x) (FD %i/%i)\n",
402           stdout,nstd[1],fileno(stdout),fileno(nstd[1]));
403    printf("hello\n");
404    write(fileno(stdout),"hellofd\n",8);
405  #endif
406
407  /* call their routine */
408  if (rtems_telnetd_config.login_check != NULL) {
409    start = rtems_shell_login_prompt(
410      stdin,
411      stderr,
412      arg->devname,
413      rtems_telnetd_config.login_check
414    );
415    login_failed = !start;
416  }
417  if (start) {
418    rtems_telnetd_config.command( arg->devname, arg->arg);
419  }
420
421  stdin  = ostd[0];
422  stdout = ostd[1];
423  stderr = ostd[2];
424
425  if (login_failed) {
426    syslog(
427      LOG_AUTHPRIV | LOG_WARNING,
428      "telnetd: to many wrong passwords entered from %s",
429      arg->peername
430    );
431  }
432
433cleanup:
434  release_a_Connection(arg->devname, arg->peername, nstd, i);
435  free(arg);
436}
437
438struct wrap_delete_args {
439  void (*t)(void *);
440  void           *a;
441};
442
443static rtems_task
444wrap_delete(rtems_task_argument arg)
445{
446  struct wrap_delete_args     *pwa = (struct wrap_delete_args *)arg;
447  register void              (*t)(void *) = pwa->t;
448  register void               *a   = pwa->a;
449
450  /* free argument before calling function (which may never return if
451   * they choose to delete themselves)
452   */
453  free(pwa);
454  t(a);
455  rtems_task_delete(RTEMS_SELF);
456}
457
458void *
459telnetd_dflt_spawn(const char *name, unsigned int priority, unsigned int stackSize, void (*fn)(void *), void* fnarg)
460{
461  rtems_status_code        sc;
462  rtems_id                 task_id = RTEMS_ID_NONE;
463  char                     nm[4] = {'X','X','X','X' };
464  struct wrap_delete_args *pwa = malloc(sizeof(*pwa));
465
466  strncpy(nm, name, 4);
467
468  if ( !pwa ) {
469    perror("Telnetd: no memory\n");
470    return (void *) RTEMS_ID_NONE;
471  }
472
473  pwa->t = fn;
474  pwa->a = fnarg;
475
476  if ((sc=rtems_task_create(
477    rtems_build_name(nm[0], nm[1], nm[2], nm[3]),
478      (rtems_task_priority)priority,
479      stackSize,
480      RTEMS_DEFAULT_MODES,
481      RTEMS_DEFAULT_ATTRIBUTES | RTEMS_FLOATING_POINT,
482      &task_id)) ||
483    (sc=rtems_task_start(
484      task_id,
485      wrap_delete,
486      (rtems_task_argument)pwa))) {
487        free(pwa);
488        rtems_error(sc,"Telnetd: spawning task failed");
489        return (void *) RTEMS_ID_NONE;
490  }
491  return (void *) task_id;
492}
493
494/* convenience routines for CEXP (retrieve stdio descriptors
495 * from reent structure)
496 */
497#ifdef stdin
498static __inline__ FILE *
499_stdin(void)  { return stdin; }
500#undef stdin
501FILE *stdin(void)  { return _stdin(); }
502#endif
503#ifdef stdout
504static __inline__ FILE *
505_stdout(void) { return stdout; }
506#undef stdout
507FILE *stdout(void) { return _stdout(); }
508#endif
509#ifdef stderr
510static __inline__ FILE *
511_stderr(void) { return stderr; }
512#undef stderr
513FILE *stderr(void) { return _stderr(); }
514#endif
515
516/* MUST NOT USE stdin & friends below here !!!!!!!!!!!!! */
Note: See TracBrowser for help on using the repository browser.