aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorDavid M. Syzdek <david@syzdek.net>2022-04-26 17:44:45 -0800
committerDavid M. Syzdek <david@syzdek.net>2022-04-29 00:41:20 -0800
commit5c71d0c8192f73cf63030da5a91e47beb40c7c6d (patch)
treeb6706b69c452a590d0a57b211847f25143820a13
parentc6f7cb947a3dd64acd6a6ff61db7a49114ce4e7e (diff)
downloadtinytac-5c71d0c8192f73cf63030da5a91e47beb40c7c6d.tar.gz
tinytac-5c71d0c8192f73cf63030da5a91e47beb40c7c6d.tar.bz2
tinytac-5c71d0c8192f73cf63030da5a91e47beb40c7c6d.tar.xz
tinytac-5c71d0c8192f73cf63030da5a91e47beb40c7c6d.zip
examples: adding stub for TACACS example
-rw-r--r--Makefile.am10
-rw-r--r--examples/tacacs-example.c1199
2 files changed, 1207 insertions, 2 deletions
diff --git a/Makefile.am b/Makefile.am
index 6a242d5..395e3ba 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -84,10 +84,11 @@ pkgdata_DATA =
84rfcdoc_DATA = 84rfcdoc_DATA =
85sbin_SCRIPTS = 85sbin_SCRIPTS =
86sbin_PROGRAMS = 86sbin_PROGRAMS =
87EXTRA_PROGRAMS = examples/md5-example 87EXTRA_PROGRAMS = examples/md5-example \
88 examples/tacacs-example \
89 src/tinytac
88EXTRA = lib/libtinytac.a \ 90EXTRA = lib/libtinytac.a \
89 lib/libtinytac.la \ 91 lib/libtinytac.la \
90 src/tinytac \
91 include/tinytac.h 92 include/tinytac.h
92 93
93 94
@@ -130,6 +131,7 @@ if ENABLE_DOCUMENATION
130endif 131endif
131if ENABLE_EXAMPLES 132if ENABLE_EXAMPLES
132 noinst_PROGRAMS += examples/md5-example 133 noinst_PROGRAMS += examples/md5-example
134 noinst_PROGRAMS += examples/tacacs-example
133endif 135endif
134if ENABLE_LIBTINYTAC 136if ENABLE_LIBTINYTAC
135 include_HEADERS += include/tinytac.h 137 include_HEADERS += include/tinytac.h
@@ -147,6 +149,10 @@ endif
147examples_md5_example_SOURCES = examples/md5-example.c 149examples_md5_example_SOURCES = examples/md5-example.c
148 150
149 151
152# macros for examples/tacacs-example
153examples_tacacs_example_SOURCES = examples/tacacs-example.c
154
155
150# macros for lib/libtinytac.a 156# macros for lib/libtinytac.a
151lib_libtinytac_a_DEPENDENCIES = Makefile \ 157lib_libtinytac_a_DEPENDENCIES = Makefile \
152 config.h 158 config.h
diff --git a/examples/tacacs-example.c b/examples/tacacs-example.c
new file mode 100644
index 0000000..a6f5124
--- /dev/null
+++ b/examples/tacacs-example.c
@@ -0,0 +1,1199 @@
1/*
2 * Tiny TACACS+ Client Library
3 * Copyright (C) 2022 David M. Syzdek <david@syzdek.net>.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * * Neither the name of David M. Syzdek nor the
16 * names of its contributors may be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DAVID M. SYZDEK BE LIABLE FOR
23 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31#define _EXAMPLES_TACACS_EXAMPLE_C 1
32
33///////////////
34// //
35// Headers //
36// //
37///////////////
38#pragma mark - Headers
39
40#include <stdio.h>
41#include <string.h>
42#include <openssl/evp.h>
43#include <assert.h>
44#include <getopt.h>
45#include <arpa/inet.h>
46#include <netdb.h>
47#include <errno.h>
48#include <sys/time.h>
49#include <pwd.h>
50#include <unistd.h>
51
52
53///////////////////
54// //
55// Definitions //
56// //
57///////////////////
58#pragma mark - Definitions
59
60#undef PROGRAM_NAME
61#define PROGRAM_NAME "tacacs-example"
62#ifndef PACKAGE_BUGREPORT
63# define PACKAGE_BUGREPORT "unknown"
64#endif
65#ifndef PACKAGE_COPYRIGHT
66# define PACKAGE_COPYRIGHT "unknown"
67#endif
68#ifndef PACKAGE_NAME
69# define PACKAGE_NAME "Tiny TACACS+ Client Library"
70#endif
71#ifndef PACKAGE_VERSION
72# define PACKAGE_VERSION "unknown"
73#endif
74
75
76#define MY_VERBOSE 0x0001U
77#define MY_QUIET 0x0002U
78#define MY_DEBUG 0x0004U
79#define MY_DEBUGMORE 0x0008U
80
81
82#ifndef SO_NOSIGPIPE
83# define SO_NOSIGPIPE SO_REUSEADDR
84#endif
85#ifndef SO_REUSEPORT
86# define SO_REUSEPORT SO_REUSEADDR
87#endif
88
89
90#define TAC_PLUS_VER 0xc1
91#define TAC_PLUS_AUTHEN 0x01
92#define TAC_PLUS_AUTHOR 0x02
93#define TAC_PLUS_ACCT 0x03
94#define TAC_PLUS_UNENCRYPTED_FLAG 0x01
95#define TAC_PLUS_SINGLE_CONNECT_FLAG 0x04
96
97#define TAC_PLUS_AUTHEN_LOGIN 0x01
98#define TAC_PLUS_AUTHEN_CHPASS 0x02
99#define TAC_PLUS_AUTHEN_SENDAUTH 0x03
100
101#define TAC_PLUS_AUTHEN_METH_NOT_SET 0x00
102#define TAC_PLUS_AUTHEN_METH_NONE 0x01
103#define TAC_PLUS_AUTHEN_METH_KRB5 0x02
104#define TAC_PLUS_AUTHEN_METH_LINE 0x03
105#define TAC_PLUS_AUTHEN_METH_ENABLE 0x04
106#define TAC_PLUS_AUTHEN_METH_LOCAL 0x05
107#define TAC_PLUS_AUTHEN_METH_TACACSPLUS 0x06
108#define TAC_PLUS_AUTHEN_METH_GUEST 0x08
109#define TAC_PLUS_AUTHEN_METH_RADIUS 0x10
110#define TAC_PLUS_AUTHEN_METH_KRB4 0x11
111#define TAC_PLUS_AUTHEN_METH_RCMD 0x20
112
113#define TAC_PLUS_AUTHEN_TYPE_ASCII 0x01
114#define TAC_PLUS_AUTHEN_TYPE_PAP 0x02
115#define TAC_PLUS_AUTHEN_TYPE_CHAP 0x03
116#define TAC_PLUS_AUTHEN_TYPE_MSCHAP 0x05
117#define TAC_PLUS_AUTHEN_TYPE_MSCHAPV2 0x06
118
119#define TAC_PLUS_AUTHEN_SVC_NONE 0x00
120#define TAC_PLUS_AUTHEN_SVC_LOGIN 0x01
121#define TAC_PLUS_AUTHEN_SVC_ENABLE 0x02
122#define TAC_PLUS_AUTHEN_SVC_PPP 0x03
123#define TAC_PLUS_AUTHEN_SVC_PT 0x05
124#define TAC_PLUS_AUTHEN_SVC_RCMD 0x06
125#define TAC_PLUS_AUTHEN_SVC_X25 0x07
126#define TAC_PLUS_AUTHEN_SVC_NASI 0x08
127#define TAC_PLUS_AUTHEN_SVC_FWPROXY 0x09
128
129#define TAC_PLUS_AUTHEN_STATUS_PASS 0x01
130#define TAC_PLUS_AUTHEN_STATUS_FAIL 0x02
131#define TAC_PLUS_AUTHEN_STATUS_GETDATA 0x03
132#define TAC_PLUS_AUTHEN_STATUS_GETUSER 0x04
133#define TAC_PLUS_AUTHEN_STATUS_GETPASS 0x05
134#define TAC_PLUS_AUTHEN_STATUS_RESTART 0x06
135#define TAC_PLUS_AUTHEN_STATUS_ERROR 0x07
136#define TAC_PLUS_AUTHEN_STATUS_FOLLOW 0x21
137
138#define TAC_PLUS_REPLY_FLAG_NOECHO 0x01
139
140#define TAC_PLUS_AUTHOR_STATUS_PASS_ADD 0x01
141#define TAC_PLUS_AUTHOR_STATUS_PASS_REPL 0x02
142#define TAC_PLUS_AUTHOR_STATUS_FAIL 0x10
143#define TAC_PLUS_AUTHOR_STATUS_ERROR 0x11
144#define TAC_PLUS_AUTHOR_STATUS_FOLLOW 0x21
145
146#define PPP_PAP_CODE_REQUEST 1
147#define PPP_PAP_CODE_ACK 2
148#define PPP_PAP_CODE_NAK 3
149
150
151/////////////////
152// //
153// Datatypes //
154// //
155/////////////////
156#pragma mark - Datatypes
157
158typedef struct tac_plus_packet
159{
160 uint8_t pckt_version; // 4 bits major and 4 bits minor
161 uint8_t pckt_type;
162 uint8_t pckt_seq_no;
163 uint8_t pckt_flags;
164 uint32_t pckt_session_id;
165 uint32_t pckt_length;
166 uint8_t pckt_body[];
167} tacplus_pckt_t;
168
169
170typedef struct tac_plus_authen_start
171{
172 uint8_t bdy_action;
173 uint8_t bdy_priv_lvl;
174 uint8_t bdy_authen_type;
175 uint8_t bdy_authen_service;
176 uint8_t bdy_user_len;
177 uint8_t bdy_port_len;
178 uint8_t bdy_rem_addr_len;
179 uint8_t bdy_data_len;
180 uint8_t bdy_bytes[];
181} authen_start_t;
182
183
184typedef struct tac_plus_authen_reply
185{
186 uint8_t bdy_status;
187 uint8_t bdy_flags;
188 uint16_t bdy_server_msg_len;
189 uint16_t bdy_data_len;
190 uint8_t bdy_bytes[];
191} authen_reply_t;
192
193
194typedef struct tac_plus_author_request
195{
196 uint8_t bdy_authen_method;
197 uint8_t bdy_priv_lvl;
198 uint8_t bdy_authen_type;
199 uint8_t bdy_authen_service;
200 uint8_t bdy_user_len;
201 uint8_t bdy_port_len;
202 uint8_t bdy_rem_addr_len;
203 uint8_t bdy_arg_cnt;
204 uint8_t bdy_bytes[];
205} author_request_t;
206
207
208typedef struct tac_plus_author_reply
209{
210 uint8_t bdy_status;
211 uint8_t bdy_arg_cnt;
212 uint16_t bdy_server_msg_len;
213 uint16_t bdy_data_len;
214 uint8_t bdy_bytes[];
215} author_reply_t;
216
217
218//////////////////
219// //
220// Prototypes //
221// //
222//////////////////
223#pragma mark - Prototypes
224
225int
226main(
227 int argc,
228 char * argv[] );
229
230
231int
232my_cmd_authen(
233 unsigned opts,
234 int s,
235 uint8_t seq_no,
236 char * key,
237 char * user );
238
239
240int
241my_cmd_author(
242 unsigned opts,
243 int s,
244 uint8_t seq_no,
245 char * key,
246 char * user,
247 int argc,
248 char ** argv );
249
250
251int
252my_connect(
253 unsigned opts,
254 const char * hostport );
255
256
257int
258my_error(
259 const char * fmt,
260 ... );
261
262
263char *
264my_ntop(
265 struct sockaddr_storage * sa,
266 char * buff,
267 size_t len );
268
269
270tacplus_pckt_t *
271my_pckt_alloc(
272 uint8_t pckt_type,
273 uint8_t seq_no,
274 size_t nbytes );
275
276
277tacplus_pckt_t *
278my_pckt_allocmore(
279 tacplus_pckt_t * pckt,
280 size_t nbytes );
281
282
283int
284my_pckt_copy_bytes(
285 void * buff,
286 size_t buff_len,
287 size_t * offp,
288 const void * bytes,
289 size_t nbytes );
290
291
292void
293my_pckt_hexdump(
294 unsigned opts,
295 tacplus_pckt_t * pckt );
296
297
298int
299my_pckt_md5pad(
300 tacplus_pckt_t * pckt,
301 char * key,
302 size_t key_len,
303 uint8_t * md5pad,
304 uint8_t * md_value );
305
306
307int
308my_pckt_obfuscate(
309 unsigned opts,
310 tacplus_pckt_t * pckt,
311 char * key,
312 size_t key_len,
313 unsigned obfuscate );
314
315
316int
317my_pckt_recv(
318 unsigned opts,
319 int s,
320 char * key,
321 tacplus_pckt_t ** pcktp );
322
323
324int
325my_pckt_send(
326 unsigned opts,
327 int s,
328 char * key,
329 tacplus_pckt_t * pckt );
330
331
332void
333my_verbose(
334 unsigned opts,
335 const char * fmt,
336 ... );
337
338
339/////////////////
340// //
341// Functions //
342// //
343/////////////////
344#pragma mark - Functions
345
346int
347main(
348 int argc,
349 char * argv[] )
350{
351 int c;
352 int s;
353 int opt_index;
354 unsigned opts;
355 char * host;
356 char * user;
357 char * key;
358 unsigned type;
359 uint8_t seq_no;
360
361 // getopt options
362 static char short_opt[] = "dhk:qVv";
363 static struct option long_opt[] =
364 {
365 {"debug", no_argument, NULL, 'd' },
366 {"help", no_argument, NULL, 'h' },
367 {"quiet", no_argument, NULL, 'q' },
368 {"silent", no_argument, NULL, 'q' },
369 {"version", no_argument, NULL, 'V' },
370 {"verbose", no_argument, NULL, 'v' },
371 { NULL, 0, NULL, 0 }
372 };
373
374 opts = 0;
375 seq_no = 0;
376 key = NULL;
377
378 while((c = getopt_long(argc, argv, short_opt, long_opt, &opt_index)) != -1)
379 {
380 switch(c)
381 {
382 case -1: /* no more arguments */
383 case 0: /* long options toggles */
384 break;
385
386 case 'd':
387 opts |= ((opts & MY_DEBUG)) ? MY_DEBUGMORE : MY_DEBUG;
388 break;
389
390 case 'h':
391 printf("Usage: %s [OPTIONS] host[:port] user\n", PROGRAM_NAME);
392 printf(" %s [OPTIONS] host[:port] user cmd arg1 ... argN\n", PROGRAM_NAME);
393 printf("Options:\n");
394 printf(" -d, --debug print debugging information\n");
395 printf(" -h, --help print this help and exit\n");
396 printf(" -k secret shared secret key\n");
397 printf(" -q, --quiet, --silent do not print messages\n");
398 printf(" -V, --version print version number and exit\n");
399 printf(" -v, --verbose print verbose messages\n");
400 printf("\n");
401 return(0);
402
403 case 'k':
404 key = optarg;
405 break;
406
407 case 'q':
408 opts |= MY_QUIET;
409 opts &= ~MY_VERBOSE;
410 break;
411
412 case 'V':
413 printf("%s (%s) %s\n", PROGRAM_NAME, PACKAGE_NAME, PACKAGE_VERSION);
414 return(0);
415
416 case 'v':
417 opts |= MY_VERBOSE;
418 opts &= ~MY_QUIET;
419 break;
420
421 case '?':
422 fprintf(stderr, "Try `%s --help' for more information.\n", PROGRAM_NAME);
423 return(1);
424
425 default:
426 fprintf(stderr, "%s: unrecognized option `--%c'\n", PROGRAM_NAME, c);
427 fprintf(stderr, "Try `%s --help' for more information.\n", PROGRAM_NAME);
428 return(1);
429 };
430 };
431 if ((optind + 2) > argc)
432 {
433 fprintf(stderr, "%s: missing required argument\n", PROGRAM_NAME);
434 fprintf(stderr, "Try `%s --help' for more information.\n", PROGRAM_NAME);
435 return(1);
436 };
437 type = ((optind + 3) < argc) ? TAC_PLUS_AUTHOR : TAC_PLUS_AUTHEN;
438 if (!(key))
439 {
440 fprintf(stderr, "%s: missing required argument `-k'\n", PROGRAM_NAME);
441 fprintf(stderr, "Try `%s --help' for more information.\n", PROGRAM_NAME);
442 return(1);
443 };
444 host = argv[optind++];
445 user = argv[optind++];
446
447 if ((s = my_connect(opts, host)) == -1)
448 return(1);
449
450 seq_no++;
451
452 if (type == TAC_PLUS_AUTHEN)
453 {
454 if (my_cmd_authen(opts, s, seq_no, key, user) != 0)
455 {
456 close(s);
457 return(1);
458 };
459 } else {
460 if (my_cmd_author(opts, s, seq_no, key, user, (argc - optind), &argv[optind]) != 0)
461 {
462 close(s);
463 return(1);
464 };
465 };
466
467 close(s);
468
469 return(0);
470}
471
472
473int
474my_cmd_authen(
475 unsigned opts,
476 int s,
477 uint8_t seq_no,
478 char * key,
479 char * user )
480{
481 tacplus_pckt_t * pckt;
482 authen_start_t * authen;
483 authen_reply_t * reply;
484 size_t nbytes;
485 size_t off;
486 size_t user_len;
487 size_t port_len;
488 size_t rem_addr_len;
489 size_t data_len;
490 char pass[_PASSWORD_LEN+1];
491 const char * msg;
492 char * str;
493
494 assert(s != -1);
495 assert(key != NULL);
496 assert(user != NULL);
497 assert(pass != NULL);
498
499 str = getpass("Enter password: ");
500 strncpy(pass, str, sizeof(pass));
501 pass[sizeof(pass)-1] = '\0';
502
503 user_len = strlen(user);
504 port_len = strlen(PROGRAM_NAME);
505 rem_addr_len = strlen("127.0.0.1");
506 data_len = strlen(pass);
507
508 nbytes = sizeof(authen_start_t); // base amount of bytes
509 nbytes += user_len;
510 nbytes += port_len; // PROGRAM_NAME is used as port
511 nbytes += rem_addr_len; // localhost is used as remote address
512 nbytes += data_len;
513
514 // create initial authen start packet
515 if ((pckt = my_pckt_alloc(TAC_PLUS_AUTHEN, seq_no, nbytes)) == NULL)
516 {
517 my_error("out of virtual memory");
518 return(1);
519 };
520 authen = (void *)pckt->pckt_body;
521 authen->bdy_action = TAC_PLUS_AUTHEN_LOGIN;
522 authen->bdy_priv_lvl = 15;
523 authen->bdy_authen_type = TAC_PLUS_AUTHEN_TYPE_PAP;
524 authen->bdy_authen_service = TAC_PLUS_AUTHEN_SVC_LOGIN;
525 authen->bdy_user_len = strlen(user);
526 authen->bdy_port_len = strlen(PROGRAM_NAME);
527 authen->bdy_rem_addr_len = strlen("127.0.0.1");
528 authen->bdy_data_len = data_len;
529
530 // copy dynamic strings into packet
531 off = 0;
532 my_pckt_copy_bytes(authen->bdy_bytes, nbytes, &off, user, user_len);
533 my_pckt_copy_bytes(authen->bdy_bytes, nbytes, &off, PROGRAM_NAME, port_len);
534 my_pckt_copy_bytes(authen->bdy_bytes, nbytes, &off, "127.0.0.1", rem_addr_len);
535 my_pckt_copy_bytes(authen->bdy_bytes, nbytes, &off, pass, data_len);
536
537 if (my_pckt_send(opts, s, key, pckt) == -1)
538 {
539 free(pckt);
540 return(1);
541 };
542 free(pckt);
543
544 if (my_pckt_recv(opts, s, key, &pckt) == -1)
545 return(1);
546 reply = (void *)pckt->pckt_body;
547 if ((opts & MY_VERBOSE))
548 {
549 switch(reply->bdy_status)
550 {
551 case TAC_PLUS_AUTHEN_STATUS_PASS: msg = "received authen status pass"; break;
552 case TAC_PLUS_AUTHEN_STATUS_FAIL: msg = "received authen status fail"; break;
553 case TAC_PLUS_AUTHEN_STATUS_GETDATA: msg = "received authen status getdata"; break;
554 case TAC_PLUS_AUTHEN_STATUS_GETUSER: msg = "received authen status getuser"; break;
555 case TAC_PLUS_AUTHEN_STATUS_GETPASS: msg = "received authen status getpass"; break;
556 case TAC_PLUS_AUTHEN_STATUS_RESTART: msg = "received authen status restart"; break;
557 case TAC_PLUS_AUTHEN_STATUS_ERROR: msg = "received authen status error"; break;
558 case TAC_PLUS_AUTHEN_STATUS_FOLLOW: msg = "received authen status follow"; break;
559 default: msg = "received authen status unknown"; break;
560 };
561 my_verbose(opts, "%s", msg);
562 if (((ntohs(reply->bdy_server_msg_len))))
563 {
564 if ((str = malloc(ntohs(reply->bdy_server_msg_len)+1)) == NULL)
565 {
566 my_error("out of virtual memory");
567 free(pckt);
568 return(-1);
569 };
570 memcpy(str, reply->bdy_bytes, ntohs(reply->bdy_server_msg_len));
571 str[ntohs(reply->bdy_server_msg_len)] = '\0';
572 my_verbose(opts, "server message: %s", str);
573 free(str);
574 };
575 };
576
577 free(pckt);
578
579 return(0);
580}
581
582
583int
584my_cmd_author(
585 unsigned opts,
586 int s,
587 uint8_t seq_no,
588 char * key,
589 char * user,
590 int argc,
591 char ** argv )
592{
593 tacplus_pckt_t * pckt;
594 author_request_t * author;
595 authen_reply_t * reply;
596 int pos;
597 size_t nbytes;
598 size_t off;
599 size_t user_len;
600 size_t port_len;
601 size_t rem_addr_len;
602 size_t data_len;
603 const char * msg;
604 char * str;
605
606 assert(s != -1);
607 assert(key != NULL);
608 assert(user != NULL);
609
610 user_len = strlen(user);
611 port_len = strlen(PROGRAM_NAME);
612 rem_addr_len = strlen("127.0.0.1");
613 data_len = strlen("service=shell") + 1;
614 data_len += strlen("cmd=") + strlen(argv[0]) + 1;
615 for(pos = 1; (pos < argc); pos++)
616 data_len += strlen("cmd-arg=") + strlen(argv[pos]) + 1;
617
618 nbytes = sizeof(authen_start_t); // base amount of bytes
619 nbytes += user_len;
620 nbytes += port_len; // PROGRAM_NAME is used as port
621 nbytes += rem_addr_len; // localhost is used as remote address
622 nbytes += data_len;
623
624 // create initial authen start packet
625 if ((pckt = my_pckt_alloc(TAC_PLUS_AUTHOR, seq_no, nbytes)) == NULL)
626 {
627 my_error("out of virtual memory");
628 return(1);
629 };
630 pckt->pckt_version = 0xc0;
631 author = (void *)pckt->pckt_body;
632 author->bdy_authen_method = TAC_PLUS_AUTHEN_METH_TACACSPLUS;
633 author->bdy_priv_lvl = 15;
634 author->bdy_authen_type = TAC_PLUS_AUTHEN_TYPE_PAP;
635 author->bdy_authen_service = TAC_PLUS_AUTHEN_SVC_LOGIN;
636 author->bdy_user_len = strlen(user);
637 author->bdy_port_len = strlen(PROGRAM_NAME);
638 author->bdy_rem_addr_len = strlen("127.0.0.1");
639 author->bdy_arg_cnt = argc + 1;
640
641 // copy dynamic strings into packet
642 off = 0;
643 author->bdy_bytes[off++] = strlen("service=shell");
644 author->bdy_bytes[off++] = strlen("cmd=") + strlen(argv[0]);
645 for(pos = 1; (pos < argc); pos++)
646 author->bdy_bytes[off++] = strlen("cmd-arg=") + strlen(argv[pos]);
647 my_pckt_copy_bytes(author->bdy_bytes, nbytes, &off, user, user_len);
648 my_pckt_copy_bytes(author->bdy_bytes, nbytes, &off, PROGRAM_NAME, port_len);
649 my_pckt_copy_bytes(author->bdy_bytes, nbytes, &off, "127.0.0.1", rem_addr_len);
650 my_pckt_copy_bytes(author->bdy_bytes, nbytes, &off, "service=shell", strlen("service=shell"));
651 my_pckt_copy_bytes(author->bdy_bytes, nbytes, &off, "cmd=", strlen("cmd="));
652 my_pckt_copy_bytes(author->bdy_bytes, nbytes, &off, argv[0], strlen(argv[0]));
653 for(pos = 1; (pos < argc); pos++)
654 {
655 my_pckt_copy_bytes(author->bdy_bytes, nbytes, &off, "cmd-arg=", strlen("cmd-arg="));
656 my_pckt_copy_bytes(author->bdy_bytes, nbytes, &off, argv[pos], strlen(argv[pos]));
657 };
658 my_pckt_copy_bytes(author->bdy_bytes, nbytes, &off, "cmd-arg=", strlen("cmd-arg="));
659
660 if (my_pckt_send(opts, s, key, pckt) == -1)
661 {
662 free(pckt);
663 return(1);
664 };
665 free(pckt);
666
667 if (my_pckt_recv(opts, s, key, &pckt) == -1)
668 return(1);
669 reply = (void *)pckt->pckt_body;
670 if ((opts & MY_VERBOSE))
671 {
672 switch(reply->bdy_status)
673 {
674 case TAC_PLUS_AUTHOR_STATUS_PASS_ADD: msg = "received author status pass add"; break;
675 case TAC_PLUS_AUTHOR_STATUS_PASS_REPL: msg = "received author status pass repl"; break;
676 case TAC_PLUS_AUTHOR_STATUS_FAIL: msg = "received author status fail"; break;
677 case TAC_PLUS_AUTHOR_STATUS_ERROR: msg = "received author status error"; break;
678 case TAC_PLUS_AUTHOR_STATUS_FOLLOW: msg = "received author status follow"; break;
679 default: msg = "received authen status unknown"; break;
680 };
681 my_verbose(opts, "%s", msg);
682 if (((ntohs(reply->bdy_server_msg_len))))
683 {
684 if ((str = malloc(ntohs(reply->bdy_server_msg_len)+1)) == NULL)
685 {
686 my_error("out of virtual memory");
687 free(pckt);
688 return(-1);
689 };
690 memcpy(str, reply->bdy_bytes, ntohs(reply->bdy_server_msg_len));
691 str[ntohs(reply->bdy_server_msg_len)] = '\0';
692 my_verbose(opts, "server message: %s", str);
693 free(str);
694 };
695 };
696
697 free(pckt);
698
699 return(0);
700}
701
702
703int
704my_connect(
705 unsigned opts,
706 const char * hostport )
707{
708 int rc;
709 int s;
710 int opt;
711 char buff[128];
712 char * str;
713 const char * host;
714 const char * port;
715 struct addrinfo hints;
716 struct addrinfo * res;
717 socklen_t sa_len;
718 struct sockaddr_storage sa;
719
720 assert(hostport != NULL);
721
722 strncpy(buff, hostport, (sizeof(buff)-1));
723 buff[sizeof(buff)-1] = '\0';
724 host = buff;
725 port = "49";
726 s = -1;
727
728 // split host and port
729 if (buff[0] == '[')
730 {
731 if ((str = strchr(buff, ']')) == NULL)
732 {
733 my_error("malformed hostname");
734 return(-1);
735 };
736 str[0] = '\0';
737 if ((str = strrchr(&str[1], ':')) == NULL)
738 {
739 my_error("malformed port");
740 return(-1);
741 };
742 port = &str[1];
743 host = &buff[1];
744 } else {
745 if ( ((str = strrchr(buff, ':')) != NULL) && (strchr(buff, ':') == str) )
746 {
747 str[0] = '\0';
748 if ((str[1]))
749 port = &str[1];
750 };
751 };
752
753 my_verbose(opts, "resolving \"%s\" on \"%s\" ...", host, port);
754 memset(&hints, 0, sizeof(struct addrinfo));
755 hints.ai_family = PF_UNSPEC;
756 hints.ai_protocol = IPPROTO_TCP;
757 hints.ai_socktype = SOCK_STREAM;
758 hints.ai_flags = AI_NUMERICSERV | AI_ADDRCONFIG;
759 if ((rc = getaddrinfo(host, port, &hints, &res)) != 0)
760 {
761 my_error("getaddrinfo(\"%s\", \"%s\"): %s", host, port, gai_strerror(rc));
762 return(s);
763 };
764 memset(&sa, 0, sizeof(struct sockaddr_storage));
765 memcpy(&sa, res->ai_addr, res->ai_addrlen);
766 sa_len = res->ai_addrlen;
767 freeaddrinfo(res);
768
769 switch(sa.ss_family)
770 {
771 case AF_INET: break;
772 case AF_INET6: break;
773 default: my_error("unsupported network family"); return(-1);
774 };
775
776 my_verbose(opts, "connecting to \"%s\" ...", my_ntop(&sa, NULL, 0));
777
778 if ((s = socket(sa.ss_family, hints.ai_socktype, 0)) == -1)
779 {
780 my_error("socket(): %s\n", strerror(errno));
781 return(s);
782 };
783
784 opt = 1; setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, (void *)&opt, sizeof(int));
785 opt = 1; setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof(int));
786 opt = 1; setsockopt(s, SOL_SOCKET, SO_REUSEPORT, (void *)&opt, sizeof(int));
787 opt = 1; setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *)&opt, sizeof(int));
788
789 if (connect(s, (struct sockaddr *)&sa, sa_len) == -1)
790 {
791 my_error("connect(): %s\n", strerror(errno));
792 close(s);
793 return(-1);
794 };
795
796 sa_len = sizeof(sa);
797 getsockname(s, (struct sockaddr *)&sa, &sa_len);
798 my_verbose(opts, "connected from \"%s\"", my_ntop(&sa, NULL, 0));
799
800 return(s);
801}
802
803
804int
805my_error(
806 const char * fmt,
807 ... )
808{
809 va_list args;
810 fprintf(stderr, "%s: ", PROGRAM_NAME);
811 va_start(args, fmt);
812 vfprintf(stderr, fmt, args);
813 va_end(args);
814 fprintf(stderr, "\n");
815 return(1);
816}
817
818
819char *
820my_ntop(
821 struct sockaddr_storage * sa,
822 char * buff,
823 size_t len )
824{
825 char addrstr[INET6_ADDRSTRLEN];
826 static char str[INET6_ADDRSTRLEN+16];
827 int port;
828
829 if (!(buff))
830 {
831 buff = str;
832 len = sizeof(str);
833 };
834
835 switch(sa->ss_family)
836 {
837 case AF_INET:
838 inet_ntop(AF_INET, &((const struct sockaddr_in *)sa)->sin_addr, addrstr, sizeof(addrstr));
839 port = ntohs(((const struct sockaddr_in *)sa)->sin_port);
840 snprintf(buff, (len-1), "%s:%i", addrstr, port);
841 break;
842
843 case AF_INET6:
844 inet_ntop(AF_INET6, &((const struct sockaddr_in6 *)sa)->sin6_addr, addrstr, sizeof(addrstr));
845 port = ntohs(((const struct sockaddr_in6 *)sa)->sin6_port);
846 snprintf(buff, (len-1), "[%s]:%i", addrstr, port);
847 break;
848
849 default:
850 return(NULL);
851 };
852 buff[len-1] = '\0';
853
854 return(buff);
855}
856
857
858tacplus_pckt_t *
859my_pckt_alloc(
860 uint8_t pckt_type,
861 uint8_t seq_no,
862 size_t nbytes )
863{
864 tacplus_pckt_t * pckt;
865 size_t size;
866 uint32_t r;
867
868 size = sizeof(tacplus_pckt_t) + nbytes;
869 switch(pckt_type)
870 {
871 case TAC_PLUS_AUTHEN: size += 8; break;
872 case TAC_PLUS_AUTHOR: size += 8; break;
873 case TAC_PLUS_ACCT: size += 9; break;
874 default:
875 my_error("invalid packet type");
876 return(NULL);
877 break;
878 };
879
880 if ((pckt = malloc(size)) == NULL)
881 {
882 my_error("out of virtual memory");
883 return(NULL);
884 };
885 memset(pckt, 0, size);
886 pckt->pckt_version = TAC_PLUS_VER;
887 pckt->pckt_type = pckt_type;
888 pckt->pckt_seq_no = seq_no;
889 pckt->pckt_flags = TAC_PLUS_UNENCRYPTED_FLAG | TAC_PLUS_SINGLE_CONNECT_FLAG;
890 pckt->pckt_length = htonl(nbytes);
891
892 r = (uint32_t)random();
893 memcpy(&pckt->pckt_session_id, &r, sizeof(uint32_t));
894
895 return(pckt);
896}
897
898
899tacplus_pckt_t *
900my_pckt_allocmore(
901 tacplus_pckt_t * pckt,
902 size_t nbytes )
903{
904 void * ptr;
905 size_t len;
906 len = ntohl(pckt->pckt_length);
907 if ((ptr = realloc(pckt, (nbytes+len))) == NULL)
908 {
909 my_error("out of virtual memory");
910 return(NULL);
911 };
912 pckt = ptr;
913 pckt->pckt_length = htonl(nbytes+len);
914 if ((len))
915 memset(&pckt->pckt_body[len], 0, nbytes);
916 return(ptr);
917}
918
919
920int
921my_pckt_copy_bytes(
922 void * dst,
923 size_t dst_size,
924 size_t * offp,
925 const void * bytes,
926 size_t nbytes )
927{
928 assert(dst != NULL);
929 assert(offp != NULL);
930 assert(bytes != NULL);
931 if (((*offp) + nbytes) > dst_size)
932 return(1);
933 memcpy((((char *)dst) + *offp), bytes, nbytes);
934 *offp += nbytes;
935 return(0);
936}
937
938
939void
940my_pckt_hexdump(
941 unsigned opts,
942 tacplus_pckt_t * pckt )
943{
944 uint8_t * bytes;
945 unsigned line;
946 unsigned pos;
947 unsigned pckt_len;
948
949 if (!(opts & MY_DEBUG))
950 return;
951 if ( (!(pckt->pckt_flags & TAC_PLUS_UNENCRYPTED_FLAG)) && (!(opts & MY_DEBUGMORE)) )
952 return;
953
954 bytes = (void *)pckt;
955 pckt_len = ntohl(pckt->pckt_length) + sizeof(tacplus_pckt_t);
956
957 printf("packet: version: %u.%u; type: ", (pckt->pckt_version >> 4), (pckt->pckt_version & 0x0f));
958 switch(pckt->pckt_type)
959 {
960 case TAC_PLUS_AUTHEN: printf("authen;"); break;
961 case TAC_PLUS_AUTHOR: printf("author;"); break;
962 case TAC_PLUS_ACCT: printf("acct;"); break;
963 default: printf("unknown;"); break;
964 };
965 printf(" seq_no: %u;", pckt->pckt_seq_no);
966 printf(" session_id: %08x;", ntohl(pckt->pckt_session_id));
967 printf("\n");
968 printf("packet: length: %u (0x%x);", pckt_len, pckt_len);
969 printf(" flags:");
970 if (!(pckt->pckt_flags))
971 printf(" NONE");
972 if ((pckt->pckt_flags & TAC_PLUS_SINGLE_CONNECT_FLAG))
973 printf(" SINGLE-CONNECT");
974 if ((pckt->pckt_flags & TAC_PLUS_UNENCRYPTED_FLAG))
975 printf(" UNENCRYPTED");
976 printf(";");
977 printf("\n");
978
979
980 printf(" offset 0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef\n");
981 for(line = 0; (line < pckt_len); line += 0x10)
982 {
983 printf("%08x", line);
984 for(pos = line; (pos < (line+0x10)); pos++)
985 {
986 if ((pos & 0x03) == 0)
987 printf(" ");
988 if (pos < pckt_len)
989 printf(" %02x", bytes[pos]);
990 else
991 printf(" ");
992 };
993 printf(" ");
994 for(pos = line; ((pos < (line+0x10)) && (pos < pckt_len)); pos++)
995 printf("%c", ((bytes[pos] < 0x20) || (bytes[pos] > 0x7e)) ? '.' : bytes[pos]);
996 printf("\n");
997 if ((line & 0xf0) == 0xf0)
998 printf("\n");
999 };
1000
1001 return;
1002}
1003
1004
1005int
1006my_pckt_md5pad(
1007 tacplus_pckt_t * pckt,
1008 char * key,
1009 size_t key_len,
1010 uint8_t * md5pad,
1011 uint8_t * md_value )
1012{
1013 unsigned md_len;
1014// unsigned char md_value[EVP_MAX_MD_SIZE];
1015 EVP_MD_CTX * mdctx;
1016
1017 assert(pckt != NULL);
1018 assert(key != NULL);
1019 assert(md_value != NULL);
1020
1021 key_len = ((key)) ? key_len : 0;
1022
1023 mdctx = EVP_MD_CTX_new();
1024 EVP_DigestInit_ex(mdctx, EVP_md5(), NULL);
1025 EVP_DigestUpdate(mdctx, &pckt->pckt_session_id, 4);
1026 EVP_DigestUpdate(mdctx, key, key_len);
1027 EVP_DigestUpdate(mdctx, &pckt->pckt_version, 1);
1028 EVP_DigestUpdate(mdctx, &pckt->pckt_seq_no, 1);
1029 if ((md5pad))
1030 EVP_DigestUpdate(mdctx, md5pad, 16);
1031 EVP_DigestFinal_ex(mdctx, md_value, &md_len);
1032 EVP_MD_CTX_free(mdctx);
1033
1034 return(0);
1035}
1036
1037
1038int
1039my_pckt_obfuscate(
1040 unsigned opts,
1041 tacplus_pckt_t * pckt,
1042 char * key,
1043 size_t key_len,
1044 unsigned obfuscate )
1045{
1046 uint8_t md_value[EVP_MAX_MD_SIZE];
1047 size_t pckt_len;
1048 size_t off;
1049 size_t pos;
1050
1051 assert(pckt != NULL);
1052 assert(key != NULL);
1053
1054 // check for existing obfuscation and flip flag
1055 obfuscate = ((obfuscate)) ? 0 : TAC_PLUS_UNENCRYPTED_FLAG;
1056 if ((pckt->pckt_flags & TAC_PLUS_UNENCRYPTED_FLAG) == obfuscate)
1057 return(0);
1058 pckt->pckt_flags ^= TAC_PLUS_UNENCRYPTED_FLAG;
1059 my_verbose(opts, "%s packet", ( ((pckt->pckt_flags&TAC_PLUS_UNENCRYPTED_FLAG)) ? "deobfuscating" : "obfuscating"));
1060
1061 // create initial pad
1062 my_pckt_md5pad(pckt, key, key_len, NULL, md_value);
1063 pckt_len = ntohl(pckt->pckt_length);
1064
1065 // apply pads to packet body
1066 for(off = 0; ((pckt_len - off) > 15); off += 16)
1067 {
1068 for(pos = 0; (pos < 16); pos++)
1069 pckt->pckt_body[off+pos] ^= md_value[pos];
1070 my_pckt_md5pad(pckt, key, key_len, md_value, md_value);
1071 };
1072 for(pos = 0; ((pos+off) < pckt_len); pos++)
1073 pckt->pckt_body[off+pos] ^= md_value[pos];
1074
1075 return(0);
1076}
1077
1078
1079int
1080my_pckt_recv(
1081 unsigned opts,
1082 int s,
1083 char * key,
1084 tacplus_pckt_t ** pcktp )
1085{
1086 size_t pckt_len;
1087 ssize_t rc;
1088 tacplus_pckt_t * pckt;
1089 void * ptr;
1090
1091 my_verbose(opts, "receiving packet ...");
1092
1093 pckt_len = sizeof(tacplus_pckt_t);
1094
1095 if ((pckt = malloc(pckt_len)) == NULL)
1096 {
1097 my_error("out of virtual memory");
1098 free(pckt);
1099 return(-1);
1100 };
1101
1102 if ((rc = read(s, pckt, pckt_len)) == -1)
1103 {
1104 my_error("recv(): %s", strerror(errno));
1105 free(pckt);
1106 return(-1);
1107 };
1108 if (rc != (ssize_t)pckt_len)
1109 {
1110 my_error("my_pckt_recv(): unable to read data");
1111 free(pckt);
1112 return(-1);
1113 };
1114
1115 pckt_len = ntohl(pckt->pckt_length) + sizeof(tacplus_pckt_t);
1116 if ((ptr = realloc(pckt, pckt_len)) == NULL)
1117 {
1118 my_error("out of virtual memory");
1119 free(pckt);
1120 return(-1);
1121 };
1122 pckt = ptr;
1123
1124 if ((rc = read(s, pckt->pckt_body, ntohl(pckt->pckt_length))) == -1)
1125 {
1126 my_error("recv(): %s", strerror(errno));
1127 free(pckt);
1128 return(-1);
1129 };
1130 if (rc != (ssize_t)ntohl(pckt->pckt_length))
1131 {
1132 my_error("my_pckt_recv(): unable to read data");
1133 free(pckt);
1134 return(-1);
1135 };
1136
1137 my_pckt_hexdump(opts, pckt);
1138 my_pckt_obfuscate(opts, pckt, key, strlen(key), 0);
1139 my_pckt_hexdump(opts, pckt);
1140
1141 *pcktp = pckt;
1142
1143 return(0);
1144}
1145
1146
1147int
1148my_pckt_send(
1149 unsigned opts,
1150 int s,
1151 char * key,
1152 tacplus_pckt_t * pckt )
1153{
1154 size_t pckt_len;
1155 ssize_t rc;
1156 const char * str;
1157
1158 my_pckt_hexdump(opts, pckt);
1159 my_pckt_obfuscate(opts, pckt, key, strlen(key), 1);
1160 my_pckt_hexdump(opts, pckt);
1161
1162 switch(pckt->pckt_type)
1163 {
1164 case TAC_PLUS_AUTHEN: str = "authen"; break;
1165 case TAC_PLUS_AUTHOR: str = "author"; break;
1166 case TAC_PLUS_ACCT: str = "acct"; break;
1167 default: str = "unknown"; break;
1168 };
1169 my_verbose(opts, "sending %s packet ...", str);
1170
1171 pckt_len = ntohl(pckt->pckt_length) + sizeof(tacplus_pckt_t);
1172 if ((rc = write(s, (void *)pckt, pckt_len)) == -1)
1173 {
1174 my_error("write(): %s", strerror(errno));
1175 return(-1);
1176 };
1177
1178 return(0);
1179}
1180
1181
1182void
1183my_verbose(
1184 unsigned opts,
1185 const char * fmt,
1186 ... )
1187{
1188 va_list args;
1189 if (!(opts & MY_VERBOSE))
1190 return;
1191 printf("%s: ", PROGRAM_NAME);
1192 va_start(args, fmt);
1193 vprintf(fmt, args);
1194 va_end(args);
1195 printf("\n");
1196 return;
1197}
1198
1199/* end of source */