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 |