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 |