1    | /***************************************
2    |   $Revision: 1.37 $
3    | 
4    |   Protocol config module (pc).  This is the protocol that the admin uses to
5    |   talk to the server.
6    | 
7    |   Status: NOT REVUED, NOT TESTED
8    | 
9    |   ******************/ /******************
10   |   Filename            : protocol_config.c
11   |   Authors             : ottrey@ripe.net - initial design
12   |                         marek@ripe.net - restructured and rewritten
13   | 
14   |   To Do               : Add a facility to take callbacks instead of
15   |                         hard-coding menu options.
16   |                         Add in all the menu support provided by the GLib
17   |                         libraries.
18   |                         (Remove strtok if multiple threads are to be used.)
19   | 			use gnu readline with expansion and history
20   |   ******************/ /******************
21   |   Copyright (c) 1999                              RIPE NCC
22   |  
23   |   All Rights Reserved
24   |   
25   |   Permission to use, copy, modify, and distribute this software and its
26   |   documentation for any purpose and without fee is hereby granted,
27   |   provided that the above copyright notice appear in all copies and that
28   |   both that copyright notice and this permission notice appear in
29   |   supporting documentation, and that the name of the author not be
30   |   used in advertising or publicity pertaining to distribution of the
31   |   software without specific, written prior permission.
32   |   
33   |   THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
34   |   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
35   |   AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
36   |   DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
37   |   AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
38   |   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
39   |   ***************************************/
40   | #include <stdio.h>
41   | #include <stdlib.h>
42   | /*** solaris' header file doesn't contain the crypt definition...
43   |      #include <unistd.h> */
44   | 
45   | extern char* crypt(const char *, const char *);   /* crypt stuff */
46   | #include <time.h>       /* Time stuff */
47   | #include <sys/ioctl.h>  /* Terminal control stuff */
48   | 
49   | #include "thread.h"
50   | #include "constants.h"
51   | #include "properties.h"
52   | #include <glib.h>
53   | 
54   | #include "sk.h"
55   | #include "ta.h"
56   | 
57   | #include "ut_string.h"
58   | #include "memwrap.h"
59   | 
60   | #include "pc_commands.h"
61   | 
62   | #define PC_IMPL
63   | #include "protocol_config.h"
64   | 
65   | 
66   | /*++++++++++++++++++++++++++++++++++++++
67   |   
68   |   Finds a command by name in the array of command function structures.
69   | 
70   |   int find_command     returns the index to the correct row of the array, or -1
71   |                        if the command could not be found.
72   | 
73   |   char *comm_name      name of the command
74   | 
75   |   Command *comm        array of command function structures.
76   |   ++++++++++++++++++++++++++++++++++++++*/
77   | static 
78   | int find_command(char *comm_name, Command *comm) 
79   | {
80   |   int i;
81   |   char *comm_buffer = wr_string(comm_name);
82   |   char *token, *cursor;
83   |   int index = -1;
84   |   
85   |   cursor = comm_buffer;
86   |   if( (token = strsep(&cursor, " \t")) != NULL) {
87   |     for (i=0; comm[i].name != NULL; i++) {
88   |       if ( strcmp(token, comm[i].name) == 0) {
89   | 	index = i;
90   | 	break;
91   |       }
92   |     }
93   |   }
94   |   
95   |   wr_free(comm_buffer);
96   | 
97   |   return index; /* returns -1 when command not found */
98   | } /* find_command() */
99   | 
100  | 
101  | int show_commands(Command *comm, char *comm_name, GString *output) 
102  | {
103  |   int i = 0;
104  | 
105  |   g_string_sprintfa(output, "%scommands are:\n\n", comm_name);
106  |   while (comm[i].name != NULL) {
107  |     g_string_sprintfa(output, "%s\t%s\n", comm[i].name, comm[i].help);
108  |     i++;
109  |   }
110  | 
111  |   return 1;
112  | } /* show_commands() */
113  | 
114  | 
115  | /*++++++++++++++++++++++++++++++++++++++
116  | 
117  |   int command_execute 
118  |                       executes a command from the given array, matching
119  | 		      given name. Passes input, output and condat to the
120  | 		      function found in the array. Command name is 
121  | 		      removed from the input line so only next words are
122  | 		      passed over to the function. 
123  | 
124  | 		      returns the code of the last command. Code 
125  | 		      PC_RET_QUIT is reserved to indicate that the connection
126  | 		      should be closed.
127  | 
128  |   Command *comm       array of command function structures (defined in
129  |                       protocol_config.h)
130  | 
131  |   char *comm_name     name of the command to be run
132  | 
133  |   char *input         rest of the command line
134  | 
135  |   GString *output     dynamically built output string
136  | 
137  |   sk_conn_st *condat  socket structure for this connection (some commands
138  |                       use it directly instead of storing the output)
139  | 
140  |   ++++++++++++++++++++++++++++++++++++++*/
141  | int command_execute(Command *comm, char *comm_name,
142  | 		    char *input, GString *output, sk_conn_st *condat) 
143  | { 
144  |   char *name, *next_word, *tmp_input;
145  |   int index, result=0;
146  | 
147  |   /* find the command in the string - first whitespace delimited word */
148  |   /* make a copy of the input */
149  |   dieif( (tmp_input = wr_string(input)) == NULL );  
150  |   next_word = tmp_input;
151  |   
152  |   /* find the first word and set the pointer to the rest of the string */
153  |   name = strsep(&next_word, " \t");
154  |   
155  |   if( name != NULL && strlen(name) != 0 ) {
156  |     index = find_command(name, comm);
157  |     if( index != -1 ) {
158  |       if( next_word != NULL ) {
159  | 	/* advance the input pointer to the next word */
160  | 	while(  *next_word != '\0' 
161  | 		&& isspace( *(unsigned char *)next_word) ) {
162  | 	  next_word++;
163  | 	}
164  |       }
165  |       else {
166  | 	next_word = "";
167  |       }
168  |       
169  |       /* run, Forrest, run...*/
170  |       result = comm[index].function(next_word, output, condat);
171  |     }
172  |     else {	    
173  |       g_string_sprintfa(output, "invalid %scommand: %s\n", comm_name, name);
174  |       show_commands(comm, comm_name, output);
175  |       result = 2;
176  |     }
177  |   }  
178  |   else {  
179  |     show_commands(comm, comm_name, output);
180  |     result = 2;
181  |   }
182  |   
183  |   free(tmp_input);
184  | 
185  |   return result;
186  | } /* command_execute() */
187  | 
188  |  
189  | 
190  | 
191  | 
192  | 
193  | 
194  | /* proces_input() */
195  | /*++++++++++++++++++++++++++++++++++++++
196  | 
197  |   Process the input. Finds the proper command in the top level command
198  |   array and invokes the function associated with it with the input and
199  |   output data as arguments.
200  | 
201  |   int process_input          returns 1 if the connection is to be kept
202  |                              or 0 when it should be finished - that is, 
203  | 			     when command_execute() returns PC_RET_QUIT.
204  | 
205  |   char *input                input (presumably a command)
206  | 
207  |   sk_conn_st *condat         connection data    
208  | 
209  |   More:
210  |   +html+ <PRE>
211  |   Author:
212  |         ottrey
213  | 	marek - changes and documentation.
214  |   +html+ </PRE>
215  |   ++++++++++++++++++++++++++++++++++++++*/
216  | static 
217  | int process_input(char *input, sk_conn_st *condat) 
218  | {
219  |   int  index;
220  |   int res=0;
221  |   GString *output = g_string_new("");
222  | 
223  |   index = find_command(input, command);
224  | 
225  |   switch (index) {
226  |   case -1:
227  |     /* Command not found */
228  |     command_help(NULL, output, condat);
229  |     break;
230  |     
231  |   default: 
232  |     res = command_execute(command, "", input, output, condat);
233  |   }
234  |   
235  |   if(res != PC_RET_QUIT) {
236  |     /*
237  |       printf("thread output=\n%s\n", output);
238  |     */
239  |     if ( CO_get_clear_screen() == 1 ) {
240  |       SK_cd_puts(condat, CLEAR_SCREEN);
241  |     }
242  |     SK_cd_puts(condat,  output->str);
243  |     SK_cd_printf(condat, "\n\n=%d= %s", res, CO_get_prompt());
244  |     
245  |   }
246  |   
247  |   g_string_free( output, TRUE );
248  | 
249  |   /* the return value is the connection state: 1=still open, 0=to be closed
250  |    */
251  | 
252  |   return (res != PC_RET_QUIT);
253  | } /* process_input() */
254  | 
255  | 
256  | /*++++++++++++++++++++++++++++++++++++++
257  |   
258  |   Authenticates the user - asks for password and checks it. The password is
259  |   echoed by the tcp stack, to disable that one would have to attach a tty
260  |   to this connection and switch to raw mode or try the hard way - renegotiate
261  |   the telnet connection to switch to character mode (and, possibly, back).
262  |   The latter has the drawback that to do it right it has to be able to check
263  |   whether there's telnet on the other side - otherwise, if the connection
264  |   is made by a program just connecting to the socket, garbage will result.
265  |   However, in such case password checking might be not a good idea.
266  | 
267  |   sk_conn_st *condat
268  | 
269  |   More:
270  |   +html+ <PRE>
271  |   Author:
272  |         ottrey
273  | 	marek - slight changes and documentation.
274  |   +html+ </PRE>
275  |   ++++++++++++++++++++++++++++++++++++++*/
276  | static 
277  | char *authenticate_user(sk_conn_st *condat) 
278  | {
279  |   char *user = NULL;
280  |   const char Salt[2] = "DB";
281  |   char input[MAX_INPUT_SIZE];
282  |   int read_result;
283  |   char *password=NULL;
284  |   char *user_password=NULL;
285  |   char user_buf[10];
286  | 
287  |   SK_cd_puts(condat, LOGIN_PROMPT);
288  |   read_result = SK_cd_gets(condat, input, MAX_INPUT_SIZE);
289  | 
290  |   strncpy(user_buf, input, 10);
291  | 
292  |   SK_cd_puts(condat, PASSWD_PROMPT);
293  |   /* XXX These aren't working.
294  |   SK_puts(sock, ECHO_ON);
295  |   echo_off(sock);
296  |   */
297  |   read_result = SK_cd_gets(condat, input, MAX_INPUT_SIZE);
298  |   /* XXX These aren't working.
299  |   echo_on(sock);
300  |   SK_puts(sock, ECHO_OFF);
301  |   */
302  | 
303  |   password = crypt(input, Salt);
304  | 
305  |   user_password = PR_get_property(user_buf, DEFAULT_USER_NAME);
306  | 
307  |   if (user_password != NULL) {
308  |     if (strcmp(password, user_password) == 0) {
309  |       /*user = (char *)calloc(1, strlen(user_buf)+1);*/
310  |       dieif( wr_malloc((void **)&user, strlen(user_buf)+1) != UT_OK);  
311  |       strcpy(user, user_buf);
312  |     }
313  |   }
314  | 
315  |   
316  |   return user;
317  | 
318  | } /* authenticate_user() */
319  | 
320  | 
321  | 
322  | /*++++++++++++++++++++++++++++++++++++++
323  |   
324  |   Main function that talks to the user connected on the given socket.
325  |   Starts by authenticating the user (if this mode is active)
326  |   and greeting her with the uptime data. Then it loops reading and executing
327  |   commands until the "quit" command (or any other command that causes
328  |   process_input to return 0).
329  | 
330  |   int sock        connected client socket
331  | 
332  |   ++++++++++++++++++++++++++++++++++++++*/
333  | void PC_interact(int sock) {
334  |   char input[MAX_INPUT_SIZE];
335  |   int connected = 1;
336  |   char *user=NULL;
337  |   sk_conn_st condat;
338  | 
339  |   memset( &condat, 0, sizeof(condat));
340  |   condat.sock = sock;
341  |   SK_getpeerip(sock, &(condat.rIP));
342  |   condat.ip = SK_getpeername(sock); /* XXX *alloc involved */
343  |   
344  |   /* Welcome the client */
345  |   SK_cd_puts(&condat, CO_get_welcome());
346  | 
347  |   /* Authenticate the user */
348  |   if (CO_get_authenticate() == 1) {
349  |     user = authenticate_user(&condat);
350  | 
351  |     if (user == NULL) {
352  |       ER_inf_va(FAC_PC, ASP_PC_I_SESSION, 
353  | 		"unsuccesful login attempt from %s", condat.ip );
354  |     }
355  |   }
356  |   else {
357  |     user="nobody";
358  |   }
359  | 
360  |   if (user != NULL) {    
361  | 
362  |     /* Log admin logging on */
363  |     ER_inf_va(FAC_PC, ASP_PC_I_SESSION, 
364  | 		"user %s from %s logged on", user, condat.ip );
365  |     
366  |     {
367  |       show_uptime("", NULL, &condat);
368  |     }
369  |     
370  |     SK_cd_printf(&condat, "=0= %s", CO_get_prompt());
371  | 
372  |     while (condat.rtc==0 && connected) {
373  |       char *icopy;
374  |       
375  |       /* Read input. Quit if no input (socket closed) */
376  |       if( SK_cd_gets(&condat, input, MAX_INPUT_SIZE) <= 0 ) {
377  | 	break;
378  |       }
379  | 
380  |       /* filter junk out: leading/trailing/redundant whitespaces */
381  |       icopy = ut_string_compress( input );
382  |       
383  |       /* set thread accounting */
384  |       TA_setactivity(icopy);
385  |       TA_increment();
386  |       
387  |       /*      if( strlen(icopy) > 0 ) {*/
388  |       {
389  | 	ER_inf_va(FAC_PC, ASP_PC_I_COMMAND, icopy);
390  | 		  
391  | 	connected = process_input(icopy, &condat);
392  |       }
393  |       
394  |       TA_setactivity("");
395  |       
396  |       free(icopy);
397  |     }
398  |     
399  |     /* Log admin logging off */
400  |     ER_inf_va(FAC_PC, ASP_PC_I_SESSION, 
401  | 		"user %s from %s logged off", user, condat.ip );
402  |     
403  |   }
404  |   
405  |   wr_free(condat.ip);
406  | } /* PC_interact() */
407  |