2 Copyright (c) 1995-1998 by Cisco systems, Inc.
4 Permission to use, copy, modify, and distribute this software for
5 any purpose and without fee is hereby granted, provided that this
6 copyright and permission notice appear on all copies of the
7 software and supporting documentation, the name of Cisco Systems,
8 Inc. not be used in advertising or publicity pertaining to
9 distribution of the program without specific prior permission, and
10 notice be given in supporting documentation that modification,
11 copying and distribution is by permission of Cisco Systems, Inc.
13 Cisco Systems, Inc. makes no representations about the suitability
14 of this software for any purpose. THIS SOFTWARE IS PROVIDED ``AS
15 IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
16 WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
17 FITNESS FOR A PARTICULAR PURPOSE.
24 #include <netinet/in.h> /* for ntohl() */
27 #ifdef HAVE_SYS_TIME_H
30 #include <sys/types.h>
42 #include "do_author.h"
45 static int write_packet TAC_ARGS((u_char *pak));
51 #define TAC_PLUS_READ_TIMEOUT 180 /* seconds */
52 #define TAC_PLUS_WRITE_TIMEOUT 180 /* seconds */
55 /* Everything to do with reading and writing packets */
57 void send_acct_reply TAC_ARGS((unsigned status, const char *msg, const char *data));
59 /* send an accounting response packet */
61 send_acct_reply(status, msg, data)
62 unsigned status; /* promoted "u_char" type */
69 struct acct_reply *reply;
70 int msg_len, data_len;
72 msg_len = msg ? strlen(msg) : 0;
73 data_len = data ? strlen(data) : 0;
75 len = TAC_PLUS_HDR_SIZE + TAC_ACCT_REPLY_FIXED_FIELDS_SIZE + msg_len + data_len;
77 pak = (u_char *) tac_malloc(len);
78 reply = (struct acct_reply *) (pak + TAC_PLUS_HDR_SIZE);
83 hdr->version = TAC_PLUS_VER_0;
84 hdr->type = TAC_PLUS_ACCT;
85 hdr->seq_no = ++session.seq_no;
86 hdr->encryption = TAC_PLUS_CLEAR;
87 hdr->session_id = htonl(session.session_id);
88 hdr->datalength = htonl(len - TAC_PLUS_HDR_SIZE);
90 reply->status = status;
91 reply->msg_len = msg_len;
92 reply->data_len = data_len;
94 p = pak + TAC_PLUS_HDR_SIZE + TAC_ACCT_REPLY_FIXED_FIELDS_SIZE;
95 bcopy(msg, p, msg_len);
98 bcopy(data, p, data_len);
100 if (debug & DEBUG_PACKET_FLAG) {
101 report(LOG_DEBUG, "Writing %s size=%d",
102 summarise_outgoing_packet_type(pak), len);
103 dump_tacacs_pak(pak);
106 reply->msg_len = ntohs(reply->msg_len);
107 reply->data_len = ntohs(reply->data_len);
113 void send_author_reply TAC_ARGS((unsigned status, const char *msg, const char *data, int arg_cnt, /* const */ char **args));
115 /* send an authorization reply packet */
117 send_author_reply(status, msg, data, arg_cnt, args)
118 unsigned status; /* promoted "u_char" type */
122 /* const */ char **args;
126 struct author_reply *reply;
132 data_len = (data ? strlen(data) : 0);
133 msg_len = (msg ? strlen(msg) : 0);
135 /* start calculating final packet size */
136 len = TAC_PLUS_HDR_SIZE + TAC_AUTHOR_REPLY_FIXED_FIELDS_SIZE + msg_len +
139 for (i=0; i < arg_cnt; i++) {
140 /* space for the arg and its length */
141 len += strlen(args[i]) + 1;
144 pak = (u_char *) tac_malloc(len);
150 reply = (struct author_reply *) (pak + TAC_PLUS_HDR_SIZE);
152 hdr->version = TAC_PLUS_VER_0;
153 hdr->type = TAC_PLUS_AUTHOR;
154 hdr->seq_no = ++session.seq_no;
155 hdr->encryption = TAC_PLUS_CLEAR;
156 hdr->session_id = htonl(session.session_id);
157 hdr->datalength = htonl(len - TAC_PLUS_HDR_SIZE);
159 reply->status = status;
160 reply->msg_len = msg_len;
161 reply->data_len = data_len;
162 reply->arg_cnt = arg_cnt;
164 p = pak + TAC_PLUS_HDR_SIZE + TAC_AUTHOR_REPLY_FIXED_FIELDS_SIZE;
166 /* place arg sizes into packet */
167 for (i=0; i < arg_cnt; i++) {
168 *p++ = strlen(args[i]);
171 bcopy(msg, p, msg_len);
174 bcopy(data, p, data_len);
177 /* copy arg bodies into packet */
178 for (i=0; i < arg_cnt; i++) {
179 int arglen = strlen(args[i]);
181 bcopy(args[i], p, arglen);
185 if (debug & DEBUG_PACKET_FLAG) {
186 report(LOG_DEBUG, "Writing %s size=%d",
187 summarise_outgoing_packet_type(pak), len);
188 dump_tacacs_pak(pak);
191 reply->msg_len = htons(reply->msg_len);
192 reply->data_len = htons(reply->data_len);
199 /* Send an authentication reply packet indicating an error has
200 occurred. msg is a null terminated character string */
202 void send_authen_error TAC_ARGS((const char *msg));
205 send_authen_error(msg)
210 sprintf(buf, "%s %s: %s", session.peer, session.port, msg);
211 report(LOG_ERR, buf);
212 send_authen_reply(TAC_PLUS_AUTHEN_STATUS_ERROR,
220 /* create and send an authentication reply packet from tacacs+ to a NAS */
222 void send_authen_reply TAC_ARGS((int status, const char *msg, unsigned msg_len, const unsigned char *data, unsigned data_len, unsigned flags));
225 send_authen_reply(status, msg, msg_len, data, data_len, flags)
228 unsigned msg_len; /* promoted "u_short" type */
229 const unsigned char *data;
230 unsigned data_len; /* promoted "u_short" type */
231 unsigned flags; /* promoted "u_char" type */
235 struct authen_reply *reply;
237 int len = TAC_PLUS_HDR_SIZE + TAC_AUTHEN_REPLY_FIXED_FIELDS_SIZE + msg_len + data_len;
239 pak = (u_char *) tac_malloc(len);
243 reply = (struct authen_reply *) (pak + TAC_PLUS_HDR_SIZE);
245 hdr->version = session.version;
246 hdr->type = TAC_PLUS_AUTHEN;
247 hdr->seq_no = ++session.seq_no;
248 hdr->encryption = TAC_PLUS_CLEAR;
249 hdr->session_id = htonl(session.session_id);
250 hdr->datalength = htonl(TAC_AUTHEN_REPLY_FIXED_FIELDS_SIZE + msg_len + data_len);
252 reply->status = status;
253 reply->msg_len = msg_len;
254 reply->data_len = data_len;
255 reply->flags = flags;
257 p = pak + TAC_PLUS_HDR_SIZE + TAC_AUTHEN_REPLY_FIXED_FIELDS_SIZE;
259 bcopy(msg, p, msg_len);
261 bcopy(data, p, data_len);
263 if (debug & DEBUG_PACKET_FLAG) {
264 report(LOG_DEBUG, "Writing %s size=%d",
265 summarise_outgoing_packet_type(pak), len);
266 dump_tacacs_pak(pak);
269 reply->msg_len = htons(reply->msg_len);
270 reply->data_len = htons(reply->data_len);
277 u_char *get_authen_continue TAC_ARGS((void));
279 /* read an authentication GETDATA packet from a NAS. Return 0 on failure */
281 get_authen_continue()
285 struct authen_cont *cont;
292 cont = (struct authen_cont *) (pak + TAC_PLUS_HDR_SIZE);
294 if ((hdr->type != TAC_PLUS_AUTHEN) || (hdr->seq_no <= 1)) {
296 "%s: Bad packet type=%d/seq no=%d when expecting authentication cont",
297 session.peer, hdr->type, hdr->seq_no);
298 report(LOG_ERR, msg);
299 send_authen_error(msg);
303 cont->user_msg_len = ntohs(cont->user_msg_len);
304 cont->user_data_len = ntohs(cont->user_data_len);
306 if ((unsigned long)(TAC_AUTHEN_CONT_FIXED_FIELDS_SIZE +
308 cont->user_data_len) !=
309 (unsigned long) ntohl(hdr->datalength)) {
310 char *m = "Illegally sized authentication cont packet";
311 report(LOG_ERR, "%s: %s", session.peer, m);
312 send_authen_error(m);
316 if (debug & DEBUG_PACKET_FLAG)
322 /* Read n bytes from descriptor fd into array ptr with timeout t
323 * seconds. Note the timeout is applied to each read, not for the
326 * Return -1 on error, eof or timeout. Otherwise return number of
329 static int sockread TAC_ARGS((int fd, u_char *ptr, int nbytes, int timeout));
332 sockread(fd, ptr, nbytes, timeout)
339 fd_set readfds, exceptfds;
342 tout.tv_sec = timeout;
346 FD_SET(fd, &readfds);
349 FD_SET(fd, &exceptfds);
354 int status = select(fd + 1, &readfds, (fd_set *) NULL,
358 report(LOG_DEBUG, "%s: timeout reading fd %d", session.peer, fd);
364 report(LOG_DEBUG, "%s: error in select %s fd %d",
365 session.peer, sys_errlist[errno], fd);
368 if (FD_ISSET(fd, &exceptfds)) {
369 report(LOG_DEBUG, "%s: exception on fd %d",
373 if (!FD_ISSET(fd, &readfds)) {
374 report(LOG_DEBUG, "%s: spurious return from select",
379 nread = read(fd, ptr, nleft);
384 report(LOG_DEBUG, "%s %s: error reading fd %d nread=%d %s",
385 session.peer, session.port, fd, nread, sys_errlist[errno]);
386 return (-1); /* error */
388 } else if (nread == 0) {
389 report(LOG_DEBUG, "%s %s: fd %d eof (connection closed)",
390 session.peer, session.port, fd);
391 return (-1); /* eof */
397 return (nbytes - nleft);
400 /* Write n bytes to descriptor fd from array ptr with timeout t
401 * seconds. Note the timeout is applied to each write, not for the
404 * Return -1 on error, eof or timeout. Otherwise return number of
407 static int sockwrite TAC_ARGS((int fd, u_char *ptr, int bytes, int timeout));
410 sockwrite(fd, ptr, bytes, timeout)
417 fd_set writefds, exceptfds;
422 tout.tv_sec = timeout;
426 FD_SET(fd, &writefds);
429 FD_SET(fd, &exceptfds);
433 while (remaining > 0) {
434 int status = select(fd + 1, (fd_set *) NULL,
435 &writefds, &exceptfds, &tout);
438 report(LOG_DEBUG, "%s: timeout writing to fd %d",
443 report(LOG_DEBUG, "%s: error in select fd %d",
447 if (FD_ISSET(fd, &exceptfds)) {
448 report(LOG_DEBUG, "%s: exception on fd %d",
450 return (sent); /* error */
453 if (!FD_ISSET(fd, &writefds)) {
454 report(LOG_DEBUG, "%s: spurious return from select",
458 sent = write(fd, ptr, remaining);
461 report(LOG_DEBUG, "%s: error writing fd %d sent=%d",
462 session.peer, fd, sent);
463 return (sent); /* error */
468 return (bytes - remaining);
471 static const char *get_session_key TAC_ARGS((void));
476 const char *retval = NULL;
478 if ((retval = cfg_get_host_key(session.peer_addr)))
480 if (session.peer_addr != session.peer
481 && (retval = cfg_get_host_key(session.peer )))
483 return (session.key);
486 /* read a packet from the wire, and decrypt it. Increment the global
487 seq_no return NULL on failure */
489 u_char *read_packet TAC_ARGS((void));
498 if (debug & DEBUG_PACKET_FLAG)
499 report(LOG_DEBUG, "Waiting for packet");
501 /* read a packet header */
502 len = sockread(session.sock, (u_char *) & hdr, TAC_PLUS_HDR_SIZE, TAC_PLUS_READ_TIMEOUT);
503 if (len != TAC_PLUS_HDR_SIZE) {
504 report(LOG_DEBUG, "Read %d bytes from %s %s, expecting %d",
505 len, session.peer, session.port, TAC_PLUS_HDR_SIZE);
509 if ((hdr.version & TAC_PLUS_MAJOR_VER_MASK) != TAC_PLUS_MAJOR_VER) {
511 "%s: Illegal major version specified: found %d wanted %d\n",
512 session.peer, hdr.version, TAC_PLUS_MAJOR_VER);
516 /* get memory for the packet */
517 len = TAC_PLUS_HDR_SIZE + ntohl(hdr.datalength);
518 if ((ntohl(hdr.datalength) & ~0xffffUL) ||
519 len < TAC_PLUS_HDR_SIZE || len > 0x10000) {
521 "%s: Illegal data size: %lu\n",
522 session.peer, (unsigned long) ntohl(hdr.datalength));
525 pkt = (u_char *) tac_malloc(len);
527 /* initialise the packet */
528 bcopy(&hdr, pkt, TAC_PLUS_HDR_SIZE);
530 /* the data start here */
531 data = pkt + TAC_PLUS_HDR_SIZE;
533 /* read the rest of the packet data */
534 if ((unsigned long)sockread(session.sock, data, ntohl(hdr.datalength),
535 TAC_PLUS_READ_TIMEOUT) !=
536 (unsigned long) ntohl(hdr.datalength)) {
537 report(LOG_ERR, "%s: start_session: bad socket read", session.peer);
540 session.seq_no++; /* should now equal that of incoming packet */
541 session.last_exch = time(NULL);
543 if (session.seq_no != hdr.seq_no) {
544 report(LOG_ERR, "%s: Illegal session seq # %d != packet seq # %d",
546 session.seq_no, hdr.seq_no);
550 /* decrypt the data portion */
551 if (md5_xor((HDR *)pkt, data, get_session_key())) {
552 report(LOG_ERR, "%s: start_session error decrypting data",
557 if (debug & DEBUG_PACKET_FLAG)
558 report(LOG_DEBUG, "Read %s size=%d",
559 summarise_incoming_packet_type(pkt), len);
561 session.version = hdr.version;
566 static int write_packet TAC_ARGS((u_char *pak));
568 /* write a packet to the wire, encrypting it */
573 HDR *hdr = (HDR *) pak;
577 len = TAC_PLUS_HDR_SIZE + ntohl(hdr->datalength);
579 /* the data start here */
580 data = pak + TAC_PLUS_HDR_SIZE;
582 /* encrypt the data portion */
583 if (md5_xor((HDR *)pak, data, get_session_key())) {
584 report(LOG_ERR, "%s: write_packet: error encrypting data", session.peer);
588 if (sockwrite(session.sock, pak, len, TAC_PLUS_WRITE_TIMEOUT) != len) {
591 session.last_exch = time(NULL);
595 void send_error_reply TAC_ARGS((int type, char *msg));
598 send_error_reply(type, msg)
603 case TAC_PLUS_AUTHEN:
604 send_authen_error(msg);
607 case TAC_PLUS_AUTHOR:
608 send_author_reply(AUTHOR_STATUS_ERROR, msg, NULL, 0, NULL);
612 send_acct_reply(TAC_PLUS_ACCT_STATUS_ERROR, msg, NULL);
616 report(LOG_ERR, "Illegal type %d for send_error_reply", type);