1 | /***************************************
2 | $Revision: 1.39 $
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,2000,2001,2002 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 |
41 | #define PC_IMPL
42 | #include "rip.h"
43 |
44 | #include <stdio.h>
45 | #include <stdlib.h>
46 | #include <time.h>
47 | #include <sys/ioctl.h>
48 | #include <glib.h>
49 |
50 | /*+ Maximum size of input that can be recieved from the client. +*/
51 | #define MAX_INPUT_SIZE 1024
52 |
53 | extern char* crypt(const char *, const char *); /* crypt stuff */
54 |
55 |
56 | /*++++++++++++++++++++++++++++++++++++++
57 |
58 | Finds a command by name in the array of command function structures.
59 |
60 | int find_command returns the index to the correct row of the array, or -1
61 | if the command could not be found.
62 |
63 | char *comm_name name of the command
64 |
65 | Command *comm array of command function structures.
66 | ++++++++++++++++++++++++++++++++++++++*/
67 | static
68 | int find_command(char *comm_name, Command *comm)
69 | {
70 | int i;
71 | char *comm_buffer = wr_string(comm_name);
72 | char *token, *cursor;
73 | int index = -1;
74 |
75 | cursor = comm_buffer;
76 | if( (token = strsep(&cursor, " \t")) != NULL) {
77 | for (i=0; comm[i].name != NULL; i++) {
78 | if ( strcmp(token, comm[i].name) == 0) {
79 | index = i;
80 | break;
81 | }
82 | }
83 | }
84 |
85 | UT_free(comm_buffer);
86 |
87 | return index; /* returns -1 when command not found */
88 | } /* find_command() */
89 |
90 |
91 | int show_commands(Command *comm, char *comm_name, GString *output)
92 | {
93 | int i = 0;
94 |
95 | g_string_sprintfa(output, "%scommands are:\n\n", comm_name);
96 | while (comm[i].name != NULL) {
97 | g_string_sprintfa(output, "%s\t%s\n", comm[i].name, comm[i].help);
98 | i++;
99 | }
100 |
101 | return 1;
102 | } /* show_commands() */
103 |
104 |
105 | /*++++++++++++++++++++++++++++++++++++++
106 |
107 | int command_execute
108 | executes a command from the given array, matching
109 | given name. Passes input, output and condat to the
110 | function found in the array. Command name is
111 | removed from the input line so only next words are
112 | passed over to the function.
113 |
114 | returns the code of the last command. Code
115 | PC_RET_QUIT is reserved to indicate that the connection
116 | should be closed.
117 |
118 | Command *comm array of command function structures (defined in
119 | protocol_config.h)
120 |
121 | char *comm_name name of the command to be run
122 |
123 | char *input rest of the command line
124 |
125 | GString *output dynamically built output string
126 |
127 | sk_conn_st *condat socket structure for this connection (some commands
128 | use it directly instead of storing the output)
129 |
130 | ++++++++++++++++++++++++++++++++++++++*/
131 | int command_execute(Command *comm, char *comm_name,
132 | char *input, GString *output, sk_conn_st *condat)
133 | {
134 | char *name, *next_word, *tmp_input;
135 | int index, result=0;
136 |
137 | /* find the command in the string - first whitespace delimited word */
138 | /* make a copy of the input */
139 | dieif( (tmp_input = wr_string(input)) == NULL );
140 | next_word = tmp_input;
141 |
142 | /* find the first word and set the pointer to the rest of the string */
143 | name = strsep(&next_word, " \t");
144 |
145 | if( name != NULL && strlen(name) != 0 ) {
146 | index = find_command(name, comm);
147 | if( index != -1 ) {
148 | if( next_word != NULL ) {
149 | /* advance the input pointer to the next word */
150 | while( *next_word != '\0'
151 | && isspace( *(unsigned char *)next_word) ) {
152 | next_word++;
153 | }
154 | }
155 | else {
156 | next_word = "";
157 | }
158 |
159 | /* run, Forrest, run...*/
160 | result = comm[index].function(next_word, output, condat);
161 | }
162 | else {
163 | g_string_sprintfa(output, "invalid %scommand: %s\n", comm_name, name);
164 | show_commands(comm, comm_name, output);
165 | result = 2;
166 | }
167 | }
168 | else {
169 | show_commands(comm, comm_name, output);
170 | result = 2;
171 | }
172 |
173 | UT_free(tmp_input);
174 |
175 | return result;
176 | } /* command_execute() */
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 | /* proces_input() */
185 | /*++++++++++++++++++++++++++++++++++++++
186 |
187 | Process the input. Finds the proper command in the top level command
188 | array and invokes the function associated with it with the input and
189 | output data as arguments.
190 |
191 | int process_input returns 1 if the connection is to be kept
192 | or 0 when it should be finished - that is,
193 | when command_execute() returns PC_RET_QUIT.
194 |
195 | char *input input (presumably a command)
196 |
197 | sk_conn_st *condat connection data
198 |
199 | More:
200 | +html+ <PRE>
201 | Author:
202 | ottrey
203 | marek - changes and documentation.
204 | +html+ </PRE>
205 | ++++++++++++++++++++++++++++++++++++++*/
206 | static
207 | int process_input(char *input, sk_conn_st *condat)
208 | {
209 | int index;
210 | int res=0;
211 | GString *output = g_string_new("");
212 |
213 | index = find_command(input, command);
214 |
215 | switch (index) {
216 | case -1:
217 | /* Command not found */
218 | command_help(NULL, output, condat);
219 | break;
220 |
221 | default:
222 | res = command_execute(command, "", input, output, condat);
223 | }
224 |
225 | if(res != PC_RET_QUIT) {
226 | /*
227 | printf("thread output=\n%s\n", output);
228 | */
229 | if ( CO_get_clear_screen() == 1 ) {
230 | SK_cd_puts(condat, CLEAR_SCREEN);
231 | }
232 | SK_cd_puts(condat, output->str);
233 | SK_cd_printf(condat, "\n\n=%d= %s", res, CO_get_prompt());
234 |
235 | }
236 |
237 | g_string_free( output, TRUE );
238 |
239 | /* the return value is the connection state: 1=still open, 0=to be closed
240 | */
241 |
242 | return (res != PC_RET_QUIT);
243 | } /* process_input() */
244 |
245 |
246 | /*++++++++++++++++++++++++++++++++++++++
247 |
248 | Authenticates the user - asks for password and checks it. The password is
249 | echoed by the tcp stack, to disable that one would have to attach a tty
250 | to this connection and switch to raw mode or try the hard way - renegotiate
251 | the telnet connection to switch to character mode (and, possibly, back).
252 | The latter has the drawback that to do it right it has to be able to check
253 | whether there's telnet on the other side - otherwise, if the connection
254 | is made by a program just connecting to the socket, garbage will result.
255 | However, in such case password checking might be not a good idea.
256 |
257 | sk_conn_st *condat
258 |
259 | More:
260 | +html+ <PRE>
261 | Author:
262 | ottrey
263 | marek - slight changes and documentation.
264 | +html+ </PRE>
265 | ++++++++++++++++++++++++++++++++++++++*/
266 | static
267 | char *authenticate_user(sk_conn_st *condat)
268 | {
269 | char *user = NULL;
270 | const char Salt[2] = "DB";
271 | char input[MAX_INPUT_SIZE];
272 | int read_result;
273 | char *password=NULL;
274 | char *user_password=NULL;
275 | char user_buf[10];
276 |
277 | SK_cd_puts(condat, LOGIN_PROMPT);
278 | read_result = SK_cd_gets(condat, input, MAX_INPUT_SIZE);
279 |
280 | strncpy(user_buf, input, 10);
281 |
282 | SK_cd_puts(condat, PASSWD_PROMPT);
283 | /* XXX These aren't working.
284 | SK_puts(sock, ECHO_ON);
285 | echo_off(sock);
286 | */
287 | read_result = SK_cd_gets(condat, input, MAX_INPUT_SIZE);
288 | /* XXX These aren't working.
289 | echo_on(sock);
290 | SK_puts(sock, ECHO_OFF);
291 | */
292 |
293 | password = crypt(input, Salt);
294 |
295 | user_password = PR_get_property(user_buf, DEFAULT_USER_NAME);
296 |
297 | if (user_password != NULL) {
298 | if (strcmp(password, user_password) == 0) {
299 | user = UT_strdup(user_buf);
300 | }
301 | }
302 |
303 |
304 | return user;
305 |
306 | } /* authenticate_user() */
307 |
308 |
309 |
310 | /*++++++++++++++++++++++++++++++++++++++
311 |
312 | Main function that talks to the user connected on the given socket.
313 | Starts by authenticating the user (if this mode is active)
314 | and greeting her with the uptime data. Then it loops reading and executing
315 | commands until the "quit" command (or any other command that causes
316 | process_input to return 0).
317 |
318 | int sock connected client socket
319 |
320 | ++++++++++++++++++++++++++++++++++++++*/
321 | void PC_interact(int sock) {
322 | char input[MAX_INPUT_SIZE];
323 | int connected = 1;
324 | char *user=NULL;
325 | sk_conn_st condat;
326 |
327 | memset( &condat, 0, sizeof(condat));
328 | condat.sock = sock;
329 | SK_getpeerip(sock, &(condat.rIP));
330 | condat.ip = SK_getpeername(sock); /* XXX *alloc involved */
331 |
332 | /* Welcome the client */
333 | SK_cd_puts(&condat, CO_get_welcome());
334 |
335 | /* Authenticate the user */
336 | if (CO_get_authenticate() == 1) {
337 | user = authenticate_user(&condat);
338 |
339 | if (user == NULL) {
340 | ER_inf_va(FAC_PC, ASP_PC_I_SESSION,
341 | "unsuccesful login attempt from %s", condat.ip );
342 | }
343 | }
344 | else {
345 | user="nobody";
346 | }
347 |
348 | if (user != NULL) {
349 |
350 | /* Log admin logging on */
351 | ER_inf_va(FAC_PC, ASP_PC_I_SESSION,
352 | "user %s from %s logged on", user, condat.ip );
353 |
354 | {
355 | show_uptime("", NULL, &condat);
356 | }
357 |
358 | SK_cd_printf(&condat, "=0= %s", CO_get_prompt());
359 |
360 | while (condat.rtc==0 && connected) {
361 | char *icopy;
362 |
363 | /* Read input. Quit if no input (socket closed) */
364 | if( SK_cd_gets(&condat, input, MAX_INPUT_SIZE) <= 0 ) {
365 | break;
366 | }
367 |
368 | /* filter junk out: leading/trailing/redundant whitespaces */
369 | icopy = ut_string_compress( input );
370 |
371 | /* set thread accounting */
372 | TA_setactivity(icopy);
373 | TA_increment();
374 |
375 | /* if( strlen(icopy) > 0 ) {*/
376 | {
377 | ER_inf_va(FAC_PC, ASP_PC_I_COMMAND, icopy);
378 |
379 | connected = process_input(icopy, &condat);
380 | }
381 |
382 | TA_setactivity("");
383 |
384 | UT_free(icopy);
385 | }
386 |
387 | /* Log admin logging off */
388 | ER_inf_va(FAC_PC, ASP_PC_I_SESSION,
389 | "user %s from %s logged off", user, condat.ip );
390 |
391 | }
392 |
393 | UT_free(condat.ip);
394 | } /* PC_interact() */
395 |