summaryrefslogtreecommitdiff
path: root/libtac/lib
diff options
context:
space:
mode:
Diffstat (limited to 'libtac/lib')
-rw-r--r--libtac/lib/acct_r.c190
-rw-r--r--libtac/lib/acct_s.c179
-rw-r--r--libtac/lib/attrib.c99
-rw-r--r--libtac/lib/authen_r.c197
-rw-r--r--libtac/lib/authen_s.c190
-rw-r--r--libtac/lib/author_r.c283
-rw-r--r--libtac/lib/author_s.c154
-rw-r--r--libtac/lib/connect.c236
-rw-r--r--libtac/lib/cont_s.c104
-rw-r--r--libtac/lib/crypt.c114
-rw-r--r--libtac/lib/hdr_check.c50
-rw-r--r--libtac/lib/header.c94
-rw-r--r--libtac/lib/magic.c88
-rw-r--r--libtac/lib/magic.h30
-rw-r--r--libtac/lib/md5.c273
-rw-r--r--libtac/lib/md5.h45
-rw-r--r--libtac/lib/messages.c31
-rw-r--r--libtac/lib/messages.h36
-rw-r--r--libtac/lib/read_wait.c136
-rw-r--r--libtac/lib/version.c24
-rw-r--r--libtac/lib/xalloc.c78
-rw-r--r--libtac/lib/xalloc.h33
22 files changed, 2664 insertions, 0 deletions
diff --git a/libtac/lib/acct_r.c b/libtac/lib/acct_r.c
new file mode 100644
index 0000000..81eeb3c
--- /dev/null
+++ b/libtac/lib/acct_r.c
@@ -0,0 +1,190 @@
+/* acct_r.c - Read accounting reply from server.
+ *
+ * Copyright (C) 2010, Pawel Krawczyk <pawel.krawczyk@hush.com> and
+ * Jeroen Nijhof <jeroen@jeroennijhof.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program - see the file COPYING.
+ *
+ * See `CHANGES' file for revision history.
+ */
+
+#include "xalloc.h"
+#include "libtac.h"
+#include "messages.h"
+
+/*
+ * return value:
+ * < 0 : error status code, see LIBTAC_STATUS_...
+ * LIBTAC_STATUS_READ_TIMEOUT
+ * LIBTAC_STATUS_SHORT_HDR
+ * LIBTAC_STATUS_SHORT_BODY
+ * LIBTAC_STATUS_PROTOCOL_ERR
+ * >= 0 : server response, see TAC_PLUS_AUTHEN_STATUS_...
+ */
+int tac_acct_read(int fd, struct areply *re) {
+ HDR th;
+ struct acct_reply *tb = NULL;
+ unsigned int len_from_header, len_from_body;
+ int r;
+ ssize_t packet_read;
+ char *msg = NULL;
+ int timeleft;
+ re->attr = NULL; /* unused */
+ re->msg = NULL;
+
+ if (tac_readtimeout_enable &&
+ tac_read_wait(fd,tac_timeout*1000, TAC_PLUS_HDR_SIZE,&timeleft) < 0 ) {
+ if (timeleft > 0) {
+ TACSYSLOG((LOG_ERR,\
+ "%s: reply error, connection closed", __func__))
+ re->status = LIBTAC_STATUS_CONN_CLOSED;
+ }
+ else {
+ TACSYSLOG((LOG_ERR,\
+ "%s: reply timeout after %d secs", __func__, tac_timeout))
+ re->status = LIBTAC_STATUS_READ_TIMEOUT;
+ }
+ re->msg = tac_xstrdup(acct_syserr_msg);
+ free(tb);
+ return re->status;
+ }
+
+ packet_read = read(fd, &th, TAC_PLUS_HDR_SIZE);
+ if(packet_read < TAC_PLUS_HDR_SIZE) {
+ if (packet_read < 0)
+ TACSYSLOG((LOG_ERR,\
+ "%s: reply header read error: %m", __func__))
+ else
+ TACSYSLOG((LOG_ERR,\
+ "%s: short reply header, read %ld of %d", __func__,\
+ packet_read, TAC_PLUS_HDR_SIZE))
+ re->msg = tac_xstrdup(acct_syserr_msg);
+ re->status = LIBTAC_STATUS_SHORT_HDR;
+ free(tb);
+ return re->status;
+ }
+
+ /* check the reply fields in header */
+ msg = _tac_check_header(&th, TAC_PLUS_ACCT);
+ if(msg != NULL) {
+ re->msg = tac_xstrdup(msg);
+ re->status = LIBTAC_STATUS_PROTOCOL_ERR;
+ free(tb);
+ TACDEBUG((LOG_DEBUG, "%s: exit status=%d, status message \"%s\"",\
+ __func__, re->status, re->msg != NULL ? re->msg : ""))
+ return re->status;
+ }
+
+ len_from_header=ntohl(th.datalength);
+ if (len_from_header > TAC_PLUS_MAX_PACKET_SIZE) {
+ TACSYSLOG((LOG_ERR,\
+ "%s: length declared in the packet %d exceeds max packet size %d",\
+ __func__,\
+ len_from_header, TAC_PLUS_MAX_PACKET_SIZE))
+ re->status=LIBTAC_STATUS_SHORT_HDR;
+ free(tb);
+ return re->status;
+ }
+ tb=(struct acct_reply *) tac_xcalloc(1, len_from_header);
+
+ /* read reply packet body */
+ if (tac_readtimeout_enable &&
+ tac_read_wait(fd,timeleft,len_from_header,&timeleft) < 0 ) {
+ if (timeleft > 0) {
+ TACSYSLOG((LOG_ERR,\
+ "%s: reply error, connection closed", __func__))
+ re->status = LIBTAC_STATUS_CONN_CLOSED;
+ }
+ else {
+ TACSYSLOG((LOG_ERR,\
+ "%s: reply timeout after %d secs", __func__, tac_timeout))
+ re->status = LIBTAC_STATUS_READ_TIMEOUT;
+ }
+ re->msg = tac_xstrdup(acct_syserr_msg);
+ free(tb);
+ return re->status;
+ }
+
+ r=read(fd, tb, len_from_header);
+ if(r < len_from_header) {
+ if (r < 0)
+ TACSYSLOG((LOG_ERR,\
+ "%s: reply body read error: %m", __func__))
+ else
+ TACSYSLOG((LOG_ERR,\
+ "%s: short reply body, read %d of %d",\
+ __func__, r, len_from_header))
+ re->msg = tac_xstrdup(acct_syserr_msg);
+ re->status = LIBTAC_STATUS_SHORT_BODY;
+ free(tb);
+ return re->status;
+ }
+
+ /* decrypt the body */
+ _tac_crypt((u_char *) tb, &th, len_from_header);
+
+ /* Convert network byte order to host byte order */
+ tb->msg_len = ntohs(tb->msg_len);
+ tb->data_len = ntohs(tb->data_len);
+
+ /* check the length fields */
+ len_from_body=sizeof(tb->msg_len) + sizeof(tb->data_len) +
+ sizeof(tb->status) + tb->msg_len + tb->data_len;
+
+ if(len_from_header != len_from_body) {
+ TACSYSLOG((LOG_ERR,\
+ "%s: inconsistent reply body, incorrect key?",\
+ __func__))
+ re->msg = tac_xstrdup(acct_syserr_msg);
+ re->status = LIBTAC_STATUS_PROTOCOL_ERR;
+ free(tb);
+ return re->status;
+ }
+
+ /* save status and clean up */
+ r=tb->status;
+ if(tb->msg_len) {
+ msg=(char *) tac_xcalloc(1, tb->msg_len+1);
+ bcopy((u_char *) tb+TAC_ACCT_REPLY_FIXED_FIELDS_SIZE, msg, tb->msg_len);
+ msg[(int)tb->msg_len] = '\0';
+ re->msg = msg; /* Freed by caller */
+ }
+
+ /* server logged our request successfully */
+ if (tb->status == TAC_PLUS_ACCT_STATUS_SUCCESS) {
+ TACDEBUG((LOG_DEBUG, "%s: accounted ok", __func__))
+ if (!re->msg) re->msg = tac_xstrdup(acct_ok_msg);
+ re->status = tb->status;
+ free(tb);
+ return re->status;
+ }
+
+ TACDEBUG((LOG_DEBUG, "%s: accounting failed, server reply status=%d",\
+ __func__, r))
+ switch(tb->status) {
+ case TAC_PLUS_ACCT_STATUS_FOLLOW:
+ re->status = tb->status;
+ if (!re->msg) re->msg=tac_xstrdup(acct_fail_msg);
+ break;
+
+ case TAC_PLUS_ACCT_STATUS_ERROR:
+ default:
+ re->status = tb->status;
+ if (!re->msg) re->msg=tac_xstrdup(acct_err_msg);
+ break;
+ }
+
+ free(tb);
+ return re->status;
+}
diff --git a/libtac/lib/acct_s.c b/libtac/lib/acct_s.c
new file mode 100644
index 0000000..d131102
--- /dev/null
+++ b/libtac/lib/acct_s.c
@@ -0,0 +1,179 @@
+/* acct_s.c - Send accounting event information to server.
+ *
+ * Copyright (C) 2010, Pawel Krawczyk <pawel.krawczyk@hush.com> and
+ * Jeroen Nijhof <jeroen@jeroennijhof.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program - see the file COPYING.
+ *
+ * See `CHANGES' file for revision history.
+ */
+
+#include "libtac.h"
+#include "xalloc.h"
+
+char *tac_acct_flag2str(int flag) {
+ switch(flag) {
+ case TAC_PLUS_ACCT_FLAG_MORE:
+ return "more";
+ case TAC_PLUS_ACCT_FLAG_START:
+ return "start";
+ case TAC_PLUS_ACCT_FLAG_STOP:
+ return "stop";
+ case TAC_PLUS_ACCT_FLAG_WATCHDOG:
+ return "update";
+ default:
+ return "unknown";
+ }
+}
+
+/*
+ * return value:
+ * 0 : success
+ * < 0 : error status code, see LIBTAC_STATUS_...
+ * LIBTAC_STATUS_WRITE_ERR
+ * LIBTAC_STATUS_WRITE_TIMEOUT (pending impl)
+ * LIBTAC_STATUS_ASSEMBLY_ERR (pending impl)
+ */
+int tac_acct_send(int fd, int type, const char *user, char *tty,
+ char *r_addr, struct tac_attrib *attr) {
+
+ HDR *th;
+ struct acct tb;
+ u_char user_len, port_len, r_addr_len;
+ struct tac_attrib *a;
+ int i = 0; /* arg count */
+ int pkt_len = 0;
+ int pktl = 0;
+ int w; /* write count */
+ u_char *pkt=NULL;
+ /* u_char *pktp; */ /* obsolute */
+ int ret = 0;
+
+ th = _tac_req_header(TAC_PLUS_ACCT, 0);
+
+ /* set header options */
+ th->version=TAC_PLUS_VER_0;
+ th->encryption=tac_encryption ? TAC_PLUS_ENCRYPTED_FLAG : TAC_PLUS_UNENCRYPTED_FLAG;
+
+ TACDEBUG((LOG_DEBUG, "%s: user '%s', tty '%s', rem_addr '%s', encrypt: %s, type: %s", \
+ __func__, user, tty, r_addr, \
+ (tac_encryption) ? "yes" : "no", \
+ tac_acct_flag2str(type)))
+
+ user_len=(u_char) strlen(user);
+ port_len=(u_char) strlen(tty);
+ r_addr_len=(u_char) strlen(r_addr);
+
+ tb.flags=(u_char) type;
+ tb.authen_method=tac_authen_method;
+ tb.priv_lvl=tac_priv_lvl;
+ if (!*tac_login) {
+ /* default to PAP */
+ tb.authen_type = TAC_PLUS_AUTHEN_TYPE_PAP;
+ } else {
+ if (strcmp(tac_login,"chap") == 0) {
+ tb.authen_type=TAC_PLUS_AUTHEN_TYPE_CHAP;
+ } else if(strcmp(tac_login,"login") == 0) {
+ tb.authen_type=TAC_PLUS_AUTHEN_TYPE_ASCII;
+ } else {
+ tb.authen_type=TAC_PLUS_AUTHEN_TYPE_PAP;
+ }
+ }
+ tb.authen_service=tac_authen_service;
+ tb.user_len=user_len;
+ tb.port_len=port_len;
+ tb.r_addr_len=r_addr_len;
+
+ /* allocate packet */
+ pkt=(u_char *) tac_xcalloc(1, TAC_ACCT_REQ_FIXED_FIELDS_SIZE);
+ pkt_len=sizeof(tb);
+
+ /* fill attribute length fields */
+ a = attr;
+ while (a) {
+ pktl = pkt_len;
+ pkt_len += sizeof(a->attr_len);
+ pkt = (u_char*) tac_xrealloc(pkt, pkt_len);
+
+ /* see comments in author_s.c
+ pktp=pkt + pkt_len;
+ pkt_len += sizeof(a->attr_len);
+ pkt = tac_xrealloc(pkt, pkt_len);
+ */
+
+ bcopy(&a->attr_len, pkt + pktl, sizeof(a->attr_len));
+ i++;
+
+ a = a->next;
+ }
+
+ /* fill the arg count field and add the fixed fields to packet */
+ tb.arg_cnt = i;
+ bcopy(&tb, pkt, TAC_ACCT_REQ_FIXED_FIELDS_SIZE);
+
+ /*
+#define PUTATTR(data, len) \
+ pktp = pkt + pkt_len; \
+ pkt_len += len; \
+ pkt = tac_xrealloc(pkt, pkt_len); \
+ bcopy(data, pktp, len);
+ */
+#define PUTATTR(data, len) \
+ pktl = pkt_len; \
+ pkt_len += len; \
+ pkt = (u_char*) tac_xrealloc(pkt, pkt_len); \
+ bcopy(data, pkt + pktl, len);
+
+ /* fill user and port fields */
+ PUTATTR(user, user_len)
+ PUTATTR(tty, port_len)
+ PUTATTR(r_addr, r_addr_len)
+
+ /* fill attributes */
+ a = attr;
+ while(a) {
+ PUTATTR(a->attr, a->attr_len)
+ a = a->next;
+ }
+
+ /* finished building packet, fill len_from_header in header */
+ th->datalength = htonl(pkt_len);
+
+ /* write header */
+ w = write(fd, th, TAC_PLUS_HDR_SIZE);
+
+ if(w < TAC_PLUS_HDR_SIZE) {
+ TACSYSLOG((LOG_ERR, "%s: short write on header, wrote %d of %d: %m",\
+ __func__, w, TAC_PLUS_HDR_SIZE))
+ free(pkt);
+ free(th);
+ return LIBTAC_STATUS_WRITE_ERR;
+ }
+
+ /* encrypt packet body */
+ _tac_crypt(pkt, th, pkt_len);
+
+ /* write body */
+ w=write(fd, pkt, pkt_len);
+ if(w < pkt_len) {
+ TACSYSLOG((LOG_ERR, "%s: short write on body, wrote %d of %d: %m",\
+ __func__, w, pkt_len))
+ ret = LIBTAC_STATUS_WRITE_ERR;
+ }
+
+ free(pkt);
+ free(th);
+ TACDEBUG((LOG_DEBUG, "%s: exit status=%d", __func__, ret))
+ return ret;
+}
diff --git a/libtac/lib/attrib.c b/libtac/lib/attrib.c
new file mode 100644
index 0000000..31ffd56
--- /dev/null
+++ b/libtac/lib/attrib.c
@@ -0,0 +1,99 @@
+/* attrib.c - Procedures for handling internal list of attributes
+ * for accounting and authorization functions.
+ *
+ * Copyright (C) 2010, Pawel Krawczyk <pawel.krawczyk@hush.com> and
+ * Jeroen Nijhof <jeroen@jeroennijhof.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program - see the file COPYING.
+ *
+ * See `CHANGES' file for revision history.
+ */
+
+#include "libtac.h"
+#include "xalloc.h"
+
+void tac_add_attrib(struct tac_attrib **attr, char *name, char *value) {
+ tac_add_attrib_pair(attr, name, '=', value);
+}
+
+void tac_add_attrib_pair(struct tac_attrib **attr, char *name, char sep, char *value) {
+ struct tac_attrib *a;
+ u_char l1 = (u_char) strlen(name);
+ u_char l2;
+ int total_len;
+
+ if (value == NULL) {
+ l2 = 0;
+ } else {
+ l2 = (u_char) strlen(value);
+ }
+ total_len = l1 + l2 + 1; /* "name" + "=" + "value" */
+
+ if (total_len > 255) {
+ TACSYSLOG((LOG_WARNING,\
+ "%s: attribute `%s' total length exceeds 255 characters, skipping",\
+ __func__, name))
+ return;
+ }
+
+ /* initialize the list if application passed us a null pointer */
+ if(*attr == NULL) {
+ *attr = (struct tac_attrib *) tac_xcalloc(1, sizeof(struct tac_attrib));
+ a = *attr;
+ } else {
+ /* find the last allocated block */
+ a = *attr;
+ while(a->next != NULL)
+ a = a->next; /* a holds last allocated block */
+
+ a->next = (struct tac_attrib *) tac_xcalloc(1,
+ sizeof(struct tac_attrib));
+ a = a->next; /* set current block pointer to the new one */
+ }
+
+ if ( sep != '=' && sep != '*' ) {
+ sep = '=';
+ }
+
+ /* fill the block */
+ a->attr_len=total_len;
+ a->attr = (char *) tac_xcalloc(1, total_len+1);
+ bcopy(name, a->attr, l1); /* paste name */
+ *(a->attr+l1)=sep; /* insert seperator "[=*]" */
+ if (value != NULL) {
+ bcopy(value, (a->attr+l1+1), l2); /* paste value */
+ }
+ *(a->attr+total_len) = '\0'; /* add 0 for safety */
+ a->next = NULL; /* make sure it's null */
+}
+
+void tac_free_attrib(struct tac_attrib **attr) {
+ struct tac_attrib *a;
+ struct tac_attrib *b;
+
+ if(*attr == NULL)
+ return;
+
+ b = *attr;
+ *attr = NULL;
+
+ /* find last allocated block */
+ do {
+ a = b;
+ b = a->next;
+ free(a->attr);
+ free(a);
+ } while (b != NULL);
+
+}
diff --git a/libtac/lib/authen_r.c b/libtac/lib/authen_r.c
new file mode 100644
index 0000000..e520420
--- /dev/null
+++ b/libtac/lib/authen_r.c
@@ -0,0 +1,197 @@
+/* authen_r.c - Read authentication reply from server.
+ *
+ * Copyright (C) 2010, Pawel Krawczyk <pawel.krawczyk@hush.com> and
+ * Jeroen Nijhof <jeroen@jeroennijhof.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program - see the file COPYING.
+ *
+ * See `CHANGES' file for revision history.
+ */
+
+#include "xalloc.h"
+#include "libtac.h"
+#include "messages.h"
+
+/* reads packet from TACACS+ server; returns:
+ * TAC_PLUS_AUTHEN_STATUS_PASS if the authentication succeded
+ * an other integer if failed. Check tacplus.h for all possible values
+ *
+ * return value:
+ * < 0 : error status code, see LIBTAC_STATUS_...
+ * LIBTAC_STATUS_READ_TIMEOUT
+ * LIBTAC_STATUS_SHORT_HDR
+ * LIBTAC_STATUS_SHORT_BODY
+ * LIBTAC_STATUS_PROTOCOL_ERR
+ * >= 0 : server response, see TAC_PLUS_AUTHEN_STATUS_...
+ */
+int tac_authen_read(int fd, struct areply *re) {
+ HDR th;
+ struct authen_reply *tb = NULL;
+ int r;
+ unsigned int len_from_header, len_from_body;
+ ssize_t packet_read;
+ char *msg = NULL;
+ int timeleft;
+
+ memset(re, 0, sizeof(struct areply));
+
+ /* read the reply header */
+ if (tac_readtimeout_enable &&
+ tac_read_wait(fd,tac_timeout*1000,TAC_PLUS_HDR_SIZE,&timeleft) < 0 ) {
+ if (timeleft > 0) {
+ TACSYSLOG((LOG_ERR,\
+ "%s: reply error, connection closed", __func__))
+ re->status = LIBTAC_STATUS_CONN_CLOSED;
+ }
+ else {
+ TACSYSLOG((LOG_ERR,\
+ "%s: reply timeout after %d secs", __func__, tac_timeout))
+ re->status=LIBTAC_STATUS_READ_TIMEOUT;
+ }
+ free(tb);
+ return re->status;
+ }
+ packet_read = read(fd, &th, TAC_PLUS_HDR_SIZE);
+ if (packet_read < TAC_PLUS_HDR_SIZE) {
+ if (packet_read < 0)
+ TACSYSLOG((LOG_ERR,\
+ "%s: reply header read error: %m", __func__))
+ else
+ TACSYSLOG((LOG_ERR,\
+ "%s: short reply header, read %d of %d",\
+ __func__, r, TAC_PLUS_HDR_SIZE))
+ re->status=LIBTAC_STATUS_READ_TIMEOUT;
+ free(tb);
+ return re->status;
+ }
+
+ /* check the reply fields in header */
+ msg = _tac_check_header(&th, TAC_PLUS_AUTHEN);
+ if(msg != NULL) {
+ re->msg = tac_xstrdup(msg);
+ re->status = LIBTAC_STATUS_PROTOCOL_ERR;
+ free(tb);
+ return re->status;
+ }
+
+ re->seq_no = th.seq_no;
+
+ len_from_header = ntohl(th.datalength);
+ if (len_from_header > TAC_PLUS_MAX_PACKET_SIZE) {
+ TACSYSLOG((LOG_ERR,\
+ "%s: length declared in the packet %d exceeds max packet size %d",\
+ __func__,\
+ len_from_header, TAC_PLUS_MAX_PACKET_SIZE))
+ re->status = LIBTAC_STATUS_PROTOCOL_ERR;
+ free(tb);
+ return re->status;
+ }
+ tb = (struct authen_reply *) tac_xcalloc(1, len_from_header);
+
+ /* read reply packet body */
+ if (tac_readtimeout_enable &&
+ tac_read_wait(fd,timeleft,len_from_header,&timeleft) < 0 ) {
+ if (timeleft > 0) {
+ TACSYSLOG((LOG_ERR,\
+ "%s: reply error, connection closed", __func__))
+ re->status = LIBTAC_STATUS_CONN_CLOSED;
+ }
+ else {
+ TACSYSLOG((LOG_ERR,\
+ "%s: reply timeout after %d secs", __func__, tac_timeout))
+ re->status=LIBTAC_STATUS_READ_TIMEOUT;
+ }
+ re->msg = tac_xstrdup(authen_syserr_msg);
+ free(tb);
+ return re->status;
+ }
+ r = read(fd, tb, len_from_header);
+ if (r < len_from_header) {
+ if (r < 0)
+ TACSYSLOG((LOG_ERR,\
+ "%s: reply body read failed: %m", __func__))
+ else
+ TACSYSLOG((LOG_ERR,\
+ "%s: short reply body, read %d of %d",\
+ __func__, r, len_from_header))
+ re->msg = tac_xstrdup(authen_syserr_msg);
+ re->status = LIBTAC_STATUS_SHORT_BODY;
+ free(tb);
+ return re->status;
+ }
+
+ /* decrypt the body */
+ _tac_crypt((u_char *) tb, &th, len_from_header);
+
+ /* Convert network byte order to host byte order */
+ tb->msg_len = ntohs(tb->msg_len);
+ tb->data_len = ntohs(tb->data_len);
+
+ /* check the length fields */
+ len_from_body = sizeof(tb->status) + sizeof(tb->flags) +
+ sizeof(tb->msg_len) + sizeof(tb->data_len) +
+ tb->msg_len + tb->data_len;
+
+ if(len_from_header != len_from_body) {
+ TACSYSLOG((LOG_ERR,\
+ "%s: inconsistent reply body, incorrect key?",\
+ __func__))
+ re->msg = tac_xstrdup(protocol_err_msg);
+ re->status = LIBTAC_STATUS_PROTOCOL_ERR;
+ free(tb);
+ return re->status;
+ }
+
+ /* save status and clean up */
+ re->status = r = tb->status;
+
+ if (0 < tb->msg_len) {
+ msg = tac_xcalloc(tb->msg_len + 1, sizeof(char));
+ memset(msg, 0, (tb->msg_len + 1));
+ memcpy(msg, (char*)tb + sizeof(struct authen_reply), tb->msg_len);
+
+ re->msg = msg;
+ }
+
+ /* server authenticated username and password successfully */
+ if (r == TAC_PLUS_AUTHEN_STATUS_PASS) {
+ TACDEBUG((LOG_DEBUG, "%s: authentication ok", __func__))
+ free(tb);
+ return re->status;
+ }
+
+ /* server ask for continue packet with password */
+ if (r == TAC_PLUS_AUTHEN_STATUS_GETPASS) {
+ TACDEBUG((LOG_DEBUG, "%s: continue packet with password needed", __func__))
+ free(tb);
+ return re->status;
+ }
+
+ /* server wants to prompt user for more data */
+ if (r == TAC_PLUS_AUTHEN_STATUS_GETDATA) {
+ re->flags = tb->flags;
+
+ TACDEBUG((LOG_DEBUG, "%s: continue packet with data request: msg=%.*s",
+ __func__, tb->msg_len, (char*)tb + sizeof(struct authen_reply)))
+ free(tb);
+ return re->status;
+ }
+
+ TACDEBUG((LOG_DEBUG, "%s: authentication failed, server reply status=%d",
+ __func__, r))
+
+ free(tb);
+ return re->status;
+} /* tac_authen_read */
+
diff --git a/libtac/lib/authen_s.c b/libtac/lib/authen_s.c
new file mode 100644
index 0000000..62c5566
--- /dev/null
+++ b/libtac/lib/authen_s.c
@@ -0,0 +1,190 @@
+/* authen_s.c - Send authentication request to the server.
+ *
+ * Copyright (C) 2010, Pawel Krawczyk <pawel.krawczyk@hush.com> and
+ * Jeroen Nijhof <jeroen@jeroennijhof.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program - see the file COPYING.
+ *
+ * See `CHANGES' file for revision history.
+ */
+
+#ifdef HAVE_CONFIG_H
+ #include "config.h"
+#endif
+
+#include "libtac.h"
+#include "xalloc.h"
+
+#if defined(HAVE_OPENSSL_MD5_H) && defined(HAVE_LIBCRYPTO)
+# include <openssl/md5.h>
+#ifndef MD5_LEN
+# define MD5_LEN MD5_LBLOCK
+#endif
+#else
+# include "md5.h"
+#endif
+
+/* this function sends a packet do TACACS+ server, asking
+ * for validation of given username and password
+ *
+ * return value:
+ * 0 : success
+ * < 0 : error status code, see LIBTAC_STATUS_...
+ * LIBTAC_STATUS_WRITE_ERR
+ * LIBTAC_STATUS_WRITE_TIMEOUT
+ * LIBTAC_STATUS_ASSEMBLY_ERR
+ */
+int tac_authen_send(int fd, const char *user, char *pass, char *tty,
+ char *r_addr, u_char action) {
+
+ HDR *th; /* TACACS+ packet header */
+ struct authen_start tb; /* message body */
+ int user_len, port_len, chal_len, mdp_len, token_len, bodylength, w;
+ int r_addr_len;
+ int pkt_len = 0;
+ int ret = 0;
+ char *chal = "1234123412341234";
+ char digest[MD5_LEN];
+ char *token = NULL;
+ u_char *pkt = NULL, *mdp = NULL;
+ MD5_CTX mdcontext;
+
+ th=_tac_req_header(TAC_PLUS_AUTHEN, 0);
+
+ /* set some header options */
+ if (!strcmp(tac_login,"login")) {
+ th->version = TAC_PLUS_VER_0;
+ } else {
+ th->version = TAC_PLUS_VER_1;
+ }
+ th->encryption = tac_encryption ? TAC_PLUS_ENCRYPTED_FLAG : TAC_PLUS_UNENCRYPTED_FLAG;
+
+ TACDEBUG((LOG_DEBUG, "%s: user '%s', tty '%s', rem_addr '%s', encrypt: %s", \
+ __func__, user, tty, r_addr, \
+ (tac_encryption) ? "yes" : "no"))
+
+ if (!strcmp(tac_login,"chap")) {
+ chal_len = strlen(chal);
+ mdp_len = sizeof(u_char) + strlen(pass) + chal_len;
+ mdp = (u_char *) tac_xcalloc(1, mdp_len);
+ mdp[0] = 5;
+ memcpy(&mdp[1], pass, strlen(pass));
+ memcpy(mdp + strlen(pass) + 1, chal, chal_len);
+#if defined(HAVE_OPENSSL_MD5_H) && defined(HAVE_LIBCRYPTO)
+ MD5_Init(&mdcontext);
+ MD5_Update(&mdcontext, mdp, mdp_len);
+ MD5_Final((u_char *) digest, &mdcontext);
+#else
+ MD5Init(&mdcontext);
+ MD5Update(&mdcontext, mdp, mdp_len);
+ MD5Final((u_char *) digest, &mdcontext);
+#endif
+ free(mdp);
+ token = (char*) tac_xcalloc(1, sizeof(u_char) + 1 + chal_len + MD5_LEN);
+ token[0] = 5;
+ memcpy(&token[1], chal, chal_len);
+ memcpy(token + chal_len + 1, digest, MD5_LEN);
+ } else {
+ token = tac_xstrdup(pass);
+ }
+
+ /* get size of submitted data */
+ user_len = strlen(user);
+ port_len = strlen(tty);
+ r_addr_len = strlen(r_addr);
+ token_len = strlen(token);
+
+ /* fill the body of message */
+ tb.action = action;
+ tb.priv_lvl = tac_priv_lvl;
+ if (!*tac_login) {
+ /* default to PAP */
+ tb.authen_type = TAC_PLUS_AUTHEN_CHPASS == action ? TAC_PLUS_AUTHEN_TYPE_ASCII : TAC_PLUS_AUTHEN_TYPE_PAP;
+ } else {
+ if (!strcmp(tac_login,"chap")) {
+ tb.authen_type = TAC_PLUS_AUTHEN_TYPE_CHAP;
+ } else if (!strcmp(tac_login,"login")) {
+ tb.authen_type = TAC_PLUS_AUTHEN_TYPE_ASCII;
+ } else {
+ tb.authen_type = TAC_PLUS_AUTHEN_TYPE_PAP;
+ }
+ }
+ tb.service = tac_authen_service;
+ tb.user_len = user_len;
+ tb.port_len = port_len;
+ tb.r_addr_len = r_addr_len; /* may be e.g Caller-ID in future */
+ tb.data_len = token_len;
+
+ /* fill body length in header */
+ bodylength = sizeof(tb) + user_len
+ + port_len + r_addr_len + token_len;
+
+ th->datalength = htonl(bodylength);
+
+ /* we can now write the header */
+ w = write(fd, th, TAC_PLUS_HDR_SIZE);
+ if (w < 0 || w < TAC_PLUS_HDR_SIZE) {
+ TACSYSLOG((LOG_ERR,\
+ "%s: short write on header, wrote %d of %d: %m",\
+ __func__, w, TAC_PLUS_HDR_SIZE))
+ free(token);
+ free(pkt);
+ free(th);
+ return LIBTAC_STATUS_WRITE_ERR;
+ }
+
+ /* build the packet */
+ pkt = (u_char *) tac_xcalloc(1, bodylength+10);
+
+ bcopy(&tb, pkt+pkt_len, sizeof(tb)); /* packet body beginning */
+ pkt_len += sizeof(tb);
+ bcopy(user, pkt+pkt_len, user_len); /* user */
+ pkt_len += user_len;
+ bcopy(tty, pkt+pkt_len, port_len); /* tty */
+ pkt_len += port_len;
+ bcopy(r_addr, pkt+pkt_len, r_addr_len); /* rem addr */
+ pkt_len += r_addr_len;
+
+ bcopy(token, pkt+pkt_len, token_len); /* password */
+ pkt_len += token_len;
+
+ /* pkt_len == bodylength ? */
+ if (pkt_len != bodylength) {
+ TACSYSLOG((LOG_ERR, "%s: bodylength %d != pkt_len %d",\
+ __func__, bodylength, pkt_len))
+ free(token);
+ free(pkt);
+ free(th);
+ return LIBTAC_STATUS_ASSEMBLY_ERR;
+ }
+
+ /* encrypt the body */
+ _tac_crypt(pkt, th, bodylength);
+
+ w = write(fd, pkt, pkt_len);
+ if (w < 0 || w < pkt_len) {
+ TACSYSLOG((LOG_ERR,\
+ "%s: short write on body, wrote %d of %d: %m",\
+ __func__, w, pkt_len))
+ ret = LIBTAC_STATUS_WRITE_ERR;
+ }
+
+ free(token);
+ free(pkt);
+ free(th);
+ TACDEBUG((LOG_DEBUG, "%s: exit status=%d", __func__, ret))
+ return ret;
+} /* tac_authen_send */
+
+
diff --git a/libtac/lib/author_r.c b/libtac/lib/author_r.c
new file mode 100644
index 0000000..ba73056
--- /dev/null
+++ b/libtac/lib/author_r.c
@@ -0,0 +1,283 @@
+/* author_r.c - Reads authorization reply from the server.
+ *
+ * Copyright (C) 2010, Pawel Krawczyk <pawel.krawczyk@hush.com> and
+ * Jeroen Nijhof <jeroen@jeroennijhof.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program - see the file COPYING.
+ *
+ * See `CHANGES' file for revision history.
+ */
+
+#include "xalloc.h"
+#include "libtac.h"
+#include "messages.h"
+
+/* This function returns structure containing
+ 1. status (granted/denied)
+ 2. message for the user
+ 3. list of attributes returned by server
+ The attributes should be applied to service authorization
+ is requested for.
+ *
+ * return value:
+ * < 0 : error status code, see LIBTAC_STATUS_...
+ * LIBTAC_STATUS_READ_TIMEOUT
+ * LIBTAC_STATUS_SHORT_HDR
+ * LIBTAC_STATUS_SHORT_BODY
+ * LIBTAC_STATUS_PROTOCOL_ERR
+ * >= 0 : server response, see TAC_PLUS_AUTHOR_STATUS_...
+ */
+int tac_author_read(int fd, struct areply *re) {
+ HDR th;
+ struct author_reply *tb = NULL;
+ unsigned int len_from_header, len_from_body;
+ int r;
+ ssize_t packet_read;
+ u_char *pktp = NULL;
+ char *msg = NULL;
+ int timeleft;
+ re->msg = NULL;
+
+ bzero(re, sizeof(struct areply));
+ if (tac_readtimeout_enable &&
+ tac_read_wait(fd,tac_timeout*1000,TAC_PLUS_HDR_SIZE,&timeleft) < 0 ) {
+ if (timeleft > 0) {
+ TACSYSLOG((LOG_ERR,\
+ "%s: reply error, connection closed", __func__))
+ re->status = LIBTAC_STATUS_CONN_CLOSED;
+ }
+ else {
+ TACSYSLOG((LOG_ERR,\
+ "%s: reply timeout after %d secs", __func__, tac_timeout))
+ re->status = LIBTAC_STATUS_READ_TIMEOUT;
+ }
+ re->msg = tac_xstrdup(author_syserr_msg);
+ free(tb);
+ return re->status;
+ }
+
+ packet_read = read(fd, &th, TAC_PLUS_HDR_SIZE);
+ if(packet_read < TAC_PLUS_HDR_SIZE) {
+ if (packet_read < 0)
+ TACSYSLOG((LOG_ERR,\
+ "%s: reply header read error: %m", __func__))
+ else
+ TACSYSLOG((LOG_ERR,\
+ "%s: short reply header, read %ld of %d", __func__,\
+ packet_read, TAC_PLUS_HDR_SIZE))
+ re->msg = tac_xstrdup(author_syserr_msg);
+ re->status = LIBTAC_STATUS_SHORT_HDR;
+ free(tb);
+ return re->status;
+ }
+
+ /* check header consistency */
+ msg = _tac_check_header(&th, TAC_PLUS_AUTHOR);
+ if (msg != NULL) {
+ /* no need to process body if header is broken */
+ re->msg = tac_xstrdup(msg);
+ re->status = LIBTAC_STATUS_PROTOCOL_ERR;
+ free(tb);
+ return re->status;
+ }
+
+ len_from_header = ntohl(th.datalength);
+ if (len_from_header > TAC_PLUS_MAX_PACKET_SIZE) {
+ TACSYSLOG((LOG_ERR,\
+ "%s: length declared in the packet %d exceeds max packet size %d",\
+ __func__,\
+ len_from_header, TAC_PLUS_MAX_PACKET_SIZE))
+ re->status = LIBTAC_STATUS_PROTOCOL_ERR;
+ free(tb);
+ return re->status;
+ }
+ tb = (struct author_reply *) tac_xcalloc(1, len_from_header);
+
+ /* read reply packet body */
+ if (tac_readtimeout_enable &&
+ tac_read_wait(fd,timeleft,len_from_header,&timeleft) < 0 ) {
+ if (timeleft > 0) {
+ TACSYSLOG((LOG_ERR,\
+ "%s: reply error, connection closed", __func__))
+ re->status = LIBTAC_STATUS_CONN_CLOSED;
+ }
+ else {
+ TACSYSLOG((LOG_ERR,\
+ "%s: reply timeout after %d secs", __func__, tac_timeout))
+ re->status = LIBTAC_STATUS_READ_TIMEOUT;
+ }
+ re->msg = tac_xstrdup(author_syserr_msg);
+ free(tb);
+ return re->status;
+ }
+ packet_read = read(fd, tb, len_from_header);
+ if (packet_read < len_from_header) {
+ if (packet_read < 0)
+ TACSYSLOG((LOG_ERR,\
+ "%s: reply body read error: %m", __func__))
+ else
+ TACSYSLOG((LOG_ERR,\
+ "%s: short reply body, read %ld of %d", __func__,\
+ packet_read, len_from_header))
+ re->msg = tac_xstrdup(author_syserr_msg);
+ re->status = LIBTAC_STATUS_SHORT_BODY;
+ free(tb);
+ return re->status;
+ }
+
+ /* decrypt the body */
+ _tac_crypt((u_char *) tb, &th, len_from_header);
+
+ /* Convert network byte order to host byte order */
+ tb->msg_len = ntohs(tb->msg_len);
+ tb->data_len = ntohs(tb->data_len);
+
+ /* check consistency of the reply body
+ * len_from_header = declared in header
+ * len_from_body = value computed from body fields
+ */
+ len_from_body = TAC_AUTHOR_REPLY_FIXED_FIELDS_SIZE +
+ tb->msg_len + tb->data_len;
+
+ pktp = (u_char *) tb + TAC_AUTHOR_REPLY_FIXED_FIELDS_SIZE;
+
+ /* cycle through the arguments supplied in the packet */
+ for (r = 0; r < tb->arg_cnt; r++) {
+ if (len_from_body > packet_read || ((void *)pktp - (void *) tb) > packet_read) {
+ TACSYSLOG((LOG_ERR,\
+ "%s: arguments supplied in packet seem to exceed its size",\
+ __func__))
+ re->msg = tac_xstrdup(protocol_err_msg);
+ re->status = LIBTAC_STATUS_PROTOCOL_ERR;
+ free(tb);
+ return re->status;
+ }
+ len_from_body += sizeof(u_char); /* add arg length field's size*/
+ len_from_body += *pktp; /* add arg length itself */
+ pktp++;
+ }
+
+ if(len_from_header != len_from_body) {
+ TACSYSLOG((LOG_ERR,\
+ "%s: inconsistent reply body, incorrect key?",\
+ __func__))
+ re->msg = tac_xstrdup(protocol_err_msg);
+ re->status = LIBTAC_STATUS_PROTOCOL_ERR;
+ free(tb);
+ return re->status;
+ }
+
+ /* packet seems to be consistent, prepare return messages */
+ /* server message for user */
+ if(tb->msg_len) {
+ char *msg = (char *) tac_xcalloc(1, tb->msg_len+1);
+ bcopy((u_char *) tb+TAC_AUTHOR_REPLY_FIXED_FIELDS_SIZE
+ + (tb->arg_cnt)*sizeof(u_char),
+ msg, tb->msg_len);
+ msg[(int) tb->msg_len] = '\0';
+ re->msg = msg; /* freed by caller */
+ }
+
+ /* server message to syslog */
+ if(tb->data_len) {
+ char *smsg=(char *) tac_xcalloc(1, tb->data_len+1);
+ bcopy((u_char *) tb + TAC_AUTHOR_REPLY_FIXED_FIELDS_SIZE
+ + (tb->arg_cnt)*sizeof(u_char)
+ + tb->msg_len, smsg,
+ tb->data_len);
+ smsg[(int) tb->data_len] = '\0';
+ TACSYSLOG((LOG_ERR, "%s: reply message: %s", __func__,smsg))
+ free(smsg);
+ }
+
+ TACDEBUG((LOG_DEBUG, "%s: authorization reply status=%d",\
+ __func__, tb->status));
+
+ /* prepare status */
+ switch(tb->status) {
+ /* success conditions */
+ /* XXX support optional vs mandatory arguments */
+ case TAC_PLUS_AUTHOR_STATUS_PASS_REPL:
+ tac_free_attrib(&re->attr);
+
+ case TAC_PLUS_AUTHOR_STATUS_PASS_ADD:
+ {
+ u_char *argp;
+
+ if(!re->msg) re->msg=tac_xstrdup(author_ok_msg);
+ re->status=tb->status;
+
+ /* add attributes received to attribute list returned to
+ the client */
+ pktp = (u_char *) tb + TAC_AUTHOR_REPLY_FIXED_FIELDS_SIZE;
+ argp = pktp + (tb->arg_cnt * sizeof(u_char)) + tb->msg_len +
+ tb->data_len;
+ TACDEBUG((LOG_DEBUG, "Args cnt %d", tb->arg_cnt));
+ /* argp points to current argument string
+ pktp points to current argument length */
+ for(r=0; r < tb->arg_cnt; r++) {
+ char buff[256];
+ char *sep;
+ char *value;
+ char sepchar = '=';
+
+ bcopy(argp, buff, (int)*pktp);
+ buff[(int)*pktp] = '\0';
+ sep = strchr(buff, '=');
+ if ( sep == NULL ) {
+ sep = strchr(buff, '*');
+ }
+ if(sep == NULL) {
+ TACSYSLOG((LOG_WARNING,\
+ "AUTHOR_STATUS_PASS_ADD/REPL: av pair does not contain a separator: %s",\
+ buff))
+ /* now buff points to attribute name, make value ""
+ treat as "name=" */
+ value = "";
+ } else {
+ sepchar = *sep;
+ *sep = '\0';
+ value = ++sep;
+ /* now buff points to attribute name,
+ value to the attribute value */
+ }
+ TACDEBUG((LOG_DEBUG, "Adding buf/value pair (%s,%s)", buff, value));
+ tac_add_attrib_pair(&re->attr, buff, sepchar, value);
+ argp += *pktp;
+ pktp++;
+ }
+ }
+ free(tb);
+ return re->status;
+ break;
+ }
+
+ switch (tb->status) {
+ /* authorization failure conditions */
+ /* failing to follow is allowed by RFC, page 23 */
+ case TAC_PLUS_AUTHOR_STATUS_FOLLOW:
+ case TAC_PLUS_AUTHOR_STATUS_FAIL:
+ if(!re->msg) re->msg = tac_xstrdup(author_fail_msg);
+ re->status=TAC_PLUS_AUTHOR_STATUS_FAIL;
+ break;
+ /* error conditions */
+ case TAC_PLUS_AUTHOR_STATUS_ERROR:
+ default:
+ if(!re->msg) re->msg = tac_xstrdup(author_err_msg);
+ re->status=TAC_PLUS_AUTHOR_STATUS_ERROR;
+ }
+
+ free(tb);
+ return re->status;
+}
diff --git a/libtac/lib/author_s.c b/libtac/lib/author_s.c
new file mode 100644
index 0000000..cc4622a
--- /dev/null
+++ b/libtac/lib/author_s.c
@@ -0,0 +1,154 @@
+/* author_s.c - Send authorization request to the server.
+ *
+ * Copyright (C) 2010, Pawel Krawczyk <pawel.krawczyk@hush.com> and
+ * Jeroen Nijhof <jeroen@jeroennijhof.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program - see the file COPYING.
+ *
+ * See `CHANGES' file for revision history.
+ */
+
+#include "libtac.h"
+#include "xalloc.h"
+
+/* Send authorization request to the server, along with attributes
+ specified in attribute list prepared with tac_add_attrib.
+ *
+ * return value:
+ * 0 : success
+ * < 0 : error status code, see LIBTAC_STATUS_...
+ * LIBTAC_STATUS_WRITE_ERR
+ * LIBTAC_STATUS_WRITE_TIMEOUT (pending impl)
+ * LIBTAC_STATUS_ASSEMBLY_ERR (pending impl)
+ */
+int tac_author_send(int fd, const char *user, char *tty, char *r_addr,
+ struct tac_attrib *attr) {
+
+ HDR *th;
+ struct author tb;
+ u_char user_len, port_len, r_addr_len;
+ struct tac_attrib *a;
+ int i = 0; /* attributes count */
+ int pkt_len = 0; /* current packet length */
+ int pktl = 0; /* temporary storage for previous pkt_len values */
+ int w; /* write() return value */
+ u_char *pkt = NULL; /* packet building pointer */
+ /* u_char *pktp; */ /* obsolete */
+ int ret = 0;
+
+ th=_tac_req_header(TAC_PLUS_AUTHOR, 0);
+
+ /* set header options */
+ th->version=TAC_PLUS_VER_0;
+ th->encryption=tac_encryption ? TAC_PLUS_ENCRYPTED_FLAG : TAC_PLUS_UNENCRYPTED_FLAG;
+
+ TACDEBUG((LOG_DEBUG, "%s: user '%s', tty '%s', rem_addr '%s', encrypt: %s", \
+ __func__, user, \
+ tty, r_addr, tac_encryption ? "yes" : "no"))
+
+ user_len = (u_char) strlen(user);
+ port_len = (u_char) strlen(tty);
+ r_addr_len = (u_char) strlen(r_addr);
+
+ tb.authen_method = tac_authen_method;
+ tb.priv_lvl = tac_priv_lvl;
+ if (!*tac_login) {
+ /* default to PAP */
+ tb.authen_type = TAC_PLUS_AUTHEN_TYPE_PAP;
+ } else {
+ if (strcmp(tac_login,"chap") == 0) {
+ tb.authen_type = TAC_PLUS_AUTHEN_TYPE_CHAP;
+ } else if (strcmp(tac_login,"login") == 0) {
+ tb.authen_type = TAC_PLUS_AUTHEN_TYPE_ASCII;
+ } else {
+ tb.authen_type = TAC_PLUS_AUTHEN_TYPE_PAP;
+ }
+ }
+ tb.service = tac_authen_service;
+ tb.user_len = user_len;
+ tb.port_len = port_len;
+ tb.r_addr_len = r_addr_len;
+
+ /* allocate packet */
+ pkt = (u_char *) tac_xcalloc(1, TAC_AUTHOR_REQ_FIXED_FIELDS_SIZE);
+ pkt_len = sizeof(tb);
+
+ /* fill attribute length fields */
+ a = attr;
+ while (a) {
+ pktl = pkt_len;
+ pkt_len += sizeof(a->attr_len);
+ pkt = (u_char*) tac_xrealloc(pkt, pkt_len);
+
+ bcopy(&a->attr_len, pkt + pktl, sizeof(a->attr_len));
+ i++;
+
+ a = a->next;
+ }
+
+ /* fill the arg count field and add the fixed fields to packet */
+ tb.arg_cnt = i;
+ bcopy(&tb, pkt, TAC_AUTHOR_REQ_FIXED_FIELDS_SIZE);
+
+#define PUTATTR(data, len) \
+ pktl = pkt_len; \
+ pkt_len += len; \
+ pkt = (u_char*) tac_xrealloc(pkt, pkt_len); \
+ bcopy(data, pkt + pktl, len);
+
+ /* fill user and port fields */
+ PUTATTR(user, user_len)
+ PUTATTR(tty, port_len)
+ PUTATTR(r_addr, r_addr_len)
+
+ /* fill attributes */
+ a = attr;
+ while (a) {
+ PUTATTR(a->attr, a->attr_len)
+
+ a = a->next;
+ }
+
+ /* finished building packet, fill len_from_header in header */
+ th->datalength = htonl(pkt_len);
+
+ /* write header */
+ w = write(fd, th, TAC_PLUS_HDR_SIZE);
+
+ if (w < TAC_PLUS_HDR_SIZE) {
+ TACSYSLOG((LOG_ERR,\
+ "%s: short write on header, wrote %d of %d: %m",\
+ __func__, w, TAC_PLUS_HDR_SIZE))
+ free(pkt);
+ free(th);
+ return LIBTAC_STATUS_WRITE_ERR;
+ }
+
+ /* encrypt packet body */
+ _tac_crypt(pkt, th, pkt_len);
+
+ /* write body */
+ w = write(fd, pkt, pkt_len);
+ if (w < pkt_len) {
+ TACSYSLOG((LOG_ERR,\
+ "%s: short write on body, wrote %d of %d: %m",\
+ __func__, w, pkt_len))
+ ret = LIBTAC_STATUS_WRITE_ERR;
+ }
+
+ free(pkt);
+ free(th);
+ TACDEBUG((LOG_DEBUG, "%s: exit status=%d", __func__, ret))
+ return ret;
+}
diff --git a/libtac/lib/connect.c b/libtac/lib/connect.c
new file mode 100644
index 0000000..8641ae2
--- /dev/null
+++ b/libtac/lib/connect.c
@@ -0,0 +1,236 @@
+/* connect.c - Open connection to server.
+ *
+ * Copyright (C) 2010, Pawel Krawczyk <pawel.krawczyk@hush.com> and
+ * Jeroen Nijhof <jeroen@jeroennijhof.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program - see the file COPYING.
+ *
+ * See `CHANGES' file for revision history.
+ */
+
+#include <signal.h>
+#include <arpa/inet.h>
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#ifdef _AIX
+#include <sys/socket.h>
+#endif
+
+#include "libtac.h"
+
+/* Pointer to TACACS+ connection timeout */
+int tac_timeout = 5;
+
+/* Returns file descriptor of open connection
+ to the first available server from list passed
+ in server table.
+
+ * return value:
+ * >= 0 : valid fd
+ * < 0 : error status code, see LIBTAC_STATUS_...
+ * iface is used to bind to an interface or VRF if non-null
+ */
+int tac_connect(struct addrinfo **server, char **key, int servers, char *iface) {
+ int tries;
+ int fd=-1;
+
+ if(servers == 0 || server == NULL) {
+ TACSYSLOG((LOG_ERR, "%s: no TACACS+ servers defined", __func__))
+ } else {
+ for ( tries = 0; tries < servers; tries++ ) {
+ if((fd=tac_connect_single(server[tries], key[tries], NULL, iface)) >= 0 ) {
+ /* tac_secret was set in tac_connect_single on success */
+ break;
+ }
+ }
+ }
+
+ /* all attempts failed if fd is still < 0 */
+ TACDEBUG((LOG_DEBUG, "%s: exit status=%d",__func__, fd))
+ return fd;
+} /* tac_connect */
+
+
+/* return value:
+ * >= 0 : valid fd
+ * < 0 : error status code, see LIBTAC_STATUS_...
+ * If iface is non-null, try to setsockopt SO_BINDTODEVICE to that iface
+ * to support specific routing, including VRF.
+ * if srcaddr is non-null, try to bind() to that address to support
+ * specifying source IP addres
+ */
+int tac_connect_single(struct addrinfo *server, const char *key,
+ struct addrinfo *srcaddr, char *iface) {
+ int retval = LIBTAC_STATUS_CONN_ERR; /* default retval */
+ int fd = -1;
+ int flags, rc;
+ fd_set readfds, writefds;
+ struct timeval tv;
+ socklen_t len;
+ struct sockaddr_storage addr;
+ char *ip;
+
+ if(server == NULL) {
+ TACSYSLOG((LOG_ERR, "%s: no TACACS+ server defined", __func__))
+ return LIBTAC_STATUS_CONN_ERR;
+ }
+
+ /* format server address into a string for use in messages */
+ ip = tac_ntop(server->ai_addr);
+
+ if((fd=socket(server->ai_family, server->ai_socktype, server->ai_protocol)) < 0) {
+ TACSYSLOG((LOG_ERR,"%s: socket creation error: %s", __func__,
+ strerror(errno)))
+ retval = LIBTAC_STATUS_CONN_ERR;
+ goto error;
+ }
+
+ if (iface) {
+ /* do not fail if the bind fails, connection may still succeed */
+ if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, iface,
+ strlen(iface)+1) < 0)
+ TACSYSLOG((LOG_WARNING, "%s: Binding socket to device %s failed: %m",
+ __func__, iface))
+ }
+
+ /* get flags for restoration later */
+ flags = fcntl(fd, F_GETFL, 0);
+
+ /* put socket in non blocking mode for timeout support */
+ if( fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1 ) {
+ TACSYSLOG((LOG_ERR, "%s: cannot set socket non blocking",\
+ __func__))
+ retval = LIBTAC_STATUS_CONN_ERR;
+ goto error;
+ }
+
+ /* bind if source address got explicity defined */
+ if (srcaddr) {
+ if (bind(fd, srcaddr->ai_addr, srcaddr->ai_addrlen) < 0) {
+ TACSYSLOG((LOG_ERR, "%s: Failed to bind source address: %s",
+ __func__, strerror(errno)))
+ retval = LIBTAC_STATUS_CONN_ERR;
+ goto error;
+ }
+ }
+
+ rc = connect(fd, server->ai_addr, server->ai_addrlen);
+ /* FIX this..for some reason errno = 0 on AIX... */
+ if((rc == -1) && (errno != EINPROGRESS) && (errno != 0)) {
+ TACSYSLOG((LOG_ERR,\
+ "%s: connection to %s failed: %m", __func__, ip))
+ retval = LIBTAC_STATUS_CONN_ERR;
+ goto error;
+ }
+
+ /* set fds for select */
+ FD_ZERO(&readfds);
+ FD_ZERO(&writefds);
+ FD_SET(fd, &readfds);
+ FD_SET(fd, &writefds);
+
+ /* set timeout seconds */
+ tv.tv_sec = tac_timeout;
+ tv.tv_usec = 0;
+
+ /* check if socket is ready for read and write */
+ rc = select(fd+1, &readfds, &writefds, NULL, &tv);
+
+ /* timeout */
+ if ( rc == 0 ) {
+ retval = LIBTAC_STATUS_CONN_TIMEOUT;
+ goto error;
+ }
+
+ /* some other error or interrupt before timeout */
+ if ( rc < 0 ) {
+ TACSYSLOG((LOG_ERR,\
+ "%s: connection failed with %s: %m", __func__, ip))
+ retval = LIBTAC_STATUS_CONN_ERR;
+ goto error;
+ }
+
+ /* check with getpeername if we have a valid connection */
+ len = sizeof addr;
+ if(getpeername(fd, (struct sockaddr*)&addr, &len) == -1) {
+ TACSYSLOG((LOG_ERR,\
+ "%s: getpeername failed with %s: %m", __func__, ip))
+ retval = LIBTAC_STATUS_CONN_ERR;
+ goto error;
+ }
+
+ /* restore flags on socket */
+ if (flags & O_NONBLOCK) {
+ flags &= ~O_NONBLOCK;
+ }
+ if(fcntl(fd, F_SETFL, flags) == -1) {
+ TACSYSLOG((LOG_ERR, "%s: cannot restore socket flags: %m",\
+ __func__))
+ retval = LIBTAC_STATUS_CONN_ERR;
+ goto error;
+ }
+
+ /* connected ok */
+ TACDEBUG((LOG_DEBUG, "%s: connected to %s", __func__, ip))
+ retval = fd;
+
+ /* set current tac_secret */
+ tac_encryption = 0;
+ if (key != NULL && *key) {
+ tac_encryption = 1;
+ tac_secret = key;
+ }
+
+error:
+ if (retval < 0 && fd >= 0) /* we had an error, don't leak fd */
+ close(fd);
+ TACDEBUG((LOG_DEBUG, "%s: exit status=%d (fd=%d)",\
+ __func__, retval < 0 ? retval:0, fd))
+ return retval;
+} /* tac_connect_single */
+
+
+/* return value:
+ * ptr to char* with format IP address
+ * warning: returns a static buffer
+ * (which some ppl don't like, but it's robust and at last no more memory leaks)
+ */
+char *tac_ntop(const struct sockaddr *sa) {
+ static char server_address[INET6_ADDRSTRLEN+16];
+
+ switch(sa->sa_family) {
+ case AF_INET:
+ inet_ntop(AF_INET, &(((struct sockaddr_in *)sa)->sin_addr),
+ server_address, INET_ADDRSTRLEN);
+
+ snprintf(server_address + strlen(server_address), 14, ":%hu",
+ htons(((struct sockaddr_in *)sa)->sin_port));
+ break;
+
+ case AF_INET6:
+ inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)sa)->sin6_addr),
+ server_address, INET6_ADDRSTRLEN);
+
+ snprintf(server_address + strlen(server_address), 14, ":%hu",
+ htons(((struct sockaddr_in6 *)sa)->sin6_port));
+ break;
+
+ default:
+ strcpy(server_address, "Unknown AF");
+ }
+ return server_address;
+} /* tac_ntop */
+
diff --git a/libtac/lib/cont_s.c b/libtac/lib/cont_s.c
new file mode 100644
index 0000000..e838d95
--- /dev/null
+++ b/libtac/lib/cont_s.c
@@ -0,0 +1,104 @@
+/* cont_s.c - Send continue request to the server.
+ *
+ * Copyright (C) 2010, Jeroen Nijhof <jeroen@jeroennijhof.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program - see the file COPYING.
+ *
+ * See `CHANGES' file for revision history.
+ */
+
+#include "libtac.h"
+#include "md5.h"
+
+/* this function sends a continue packet do TACACS+ server, asking
+ * for validation of given password
+ *
+ * return value:
+ * 0 : success
+ * < 0 : error status code, see LIBTAC_STATUS_...
+ * LIBTAC_STATUS_WRITE_ERR
+ * LIBTAC_STATUS_WRITE_TIMEOUT (pending impl)
+ * LIBTAC_STATUS_ASSEMBLY_ERR
+ */
+int tac_cont_send_seq(int fd, char *pass, int seq) {
+ HDR *th; /* TACACS+ packet header */
+ struct authen_cont tb; /* continue body */
+ int pass_len, bodylength, w;
+ int pkt_len = 0;
+ int ret = 0;
+ u_char *pkt = NULL;
+
+ th = _tac_req_header(TAC_PLUS_AUTHEN, 1);
+
+ /* set some header options */
+ th->version = TAC_PLUS_VER_0;
+ th->seq_no = seq; /* 1 = request, 2 = reply, 3 = continue, 4 = reply */
+ th->encryption = tac_encryption ? TAC_PLUS_ENCRYPTED_FLAG : TAC_PLUS_UNENCRYPTED_FLAG;
+
+ /* get size of submitted data */
+ pass_len = strlen(pass);
+
+ /* fill the body of message */
+ tb.user_msg_len = htons(pass_len);
+ tb.user_data_len = tb.flags = 0;
+
+ /* fill body length in header */
+ bodylength = TAC_AUTHEN_CONT_FIXED_FIELDS_SIZE+0+pass_len;
+
+ th->datalength = htonl(bodylength);
+
+ /* we can now write the header */
+ w = write(fd, th, TAC_PLUS_HDR_SIZE);
+ if (w < 0 || w < TAC_PLUS_HDR_SIZE) {
+ TACSYSLOG((LOG_ERR, "%s: short write on header, wrote %d of %d: %m",\
+ __func__, w, TAC_PLUS_HDR_SIZE))
+ free(pkt);
+ free(th);
+ return LIBTAC_STATUS_WRITE_ERR;
+ }
+
+ /* build the packet */
+ pkt = (u_char *) tac_xcalloc(1, bodylength);
+
+ bcopy(&tb, pkt+pkt_len, TAC_AUTHEN_CONT_FIXED_FIELDS_SIZE); /* packet body beginning */
+ pkt_len += TAC_AUTHEN_CONT_FIXED_FIELDS_SIZE;
+ bcopy(pass, pkt+pkt_len, pass_len); /* password */
+ pkt_len += pass_len;
+
+ /* pkt_len == bodylength ? */
+ if (pkt_len != bodylength) {
+ TACSYSLOG((LOG_ERR,\
+ "%s: bodylength %d != pkt_len %d",\
+ __func__, bodylength, pkt_len))
+ free(pkt);
+ free(th);
+ return LIBTAC_STATUS_ASSEMBLY_ERR;
+ }
+
+ /* encrypt the body */
+ _tac_crypt(pkt, th, bodylength);
+
+ w = write(fd, pkt, pkt_len);
+ if (w < 0 || w < pkt_len) {
+ TACSYSLOG((LOG_ERR,\
+ "%s: short write on body, wrote %d of %d: %m",\
+ __func__, w, pkt_len))
+ ret=LIBTAC_STATUS_WRITE_ERR;
+ }
+
+ free(pkt);
+ free(th);
+ TACDEBUG((LOG_DEBUG, "%s: exit status=%d", __func__, ret))
+ return ret;
+} /* tac_cont_send */
diff --git a/libtac/lib/crypt.c b/libtac/lib/crypt.c
new file mode 100644
index 0000000..49a071c
--- /dev/null
+++ b/libtac/lib/crypt.c
@@ -0,0 +1,114 @@
+/* crypt.c - TACACS+ encryption related functions
+ *
+ * Copyright (C) 2010, Pawel Krawczyk <pawel.krawczyk@hush.com> and
+ * Jeroen Nijhof <jeroen@jeroennijhof.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program - see the file COPYING.
+ *
+ * See `CHANGES' file for revision history.
+ */
+
+#include "libtac.h"
+#include "xalloc.h"
+
+#ifdef HAVE_CONFIG_H
+ #include "config.h"
+#endif
+
+#if defined(HAVE_OPENSSL_MD5_H) && defined(HAVE_LIBCRYPTO)
+# include <openssl/md5.h>
+#else
+# include "md5.h"
+# ifndef MD5_LBLOCK /* should be always, since it's in openssl */
+# define MD5_LBLOCK MD5_LEN
+# endif
+#endif
+
+/* Produce MD5 pseudo-random pad for TACACS+ encryption.
+ Use data from packet header and secret, which
+ should be a global variable */
+u_char *_tac_md5_pad(int len, HDR *hdr) {
+ int n, i, bufsize;
+ int bp = 0; /* buffer pointer */
+ int pp = 0; /* pad pointer */
+ u_char *pad;
+ u_char *buf;
+ MD5_CTX mdcontext;
+
+ /* make pseudo pad */
+ n = (int)(len/16)+1; /* number of MD5 runs */
+ bufsize = sizeof(hdr->session_id) + strlen(tac_secret) + sizeof(hdr->version)
+ + sizeof(hdr->seq_no) + MD5_LBLOCK + 10;
+ buf = (u_char *) tac_xcalloc(1, bufsize);
+ pad = (u_char *) tac_xcalloc(n, MD5_LBLOCK);
+
+ for (i=0; i<n; i++) {
+ /* MD5_1 = MD5{session_id, secret, version, seq_no}
+ MD5_2 = MD5{session_id, secret, version, seq_no, MD5_1} */
+
+ /* place session_id, key, version and seq_no in buffer */
+ bp = 0;
+ bcopy(&hdr->session_id, buf, sizeof(session_id));
+ bp += sizeof(session_id);
+ bcopy(tac_secret, buf+bp, strlen(tac_secret));
+ bp += strlen(tac_secret);
+ bcopy(&hdr->version, buf+bp, sizeof(hdr->version));
+ bp += sizeof(hdr->version);
+ bcopy(&hdr->seq_no, buf+bp, sizeof(hdr->seq_no));
+ bp += sizeof(hdr->seq_no);
+
+ /* append previous pad if this is not the first run */
+ if (i) {
+ bcopy(pad+((i-1)*MD5_LBLOCK), buf+bp, MD5_LBLOCK);
+ bp+=MD5_LBLOCK;
+ }
+
+#if defined(HAVE_OPENSSL_MD5_H) && defined(HAVE_LIBCRYPTO)
+ MD5_Init(&mdcontext);
+ MD5_Update(&mdcontext, buf, bp);
+ MD5_Final(pad+pp, &mdcontext);
+#else
+ MD5Init(&mdcontext);
+ MD5Update(&mdcontext, buf, bp);
+ MD5Final(pad+pp, &mdcontext);
+#endif
+
+ pp += MD5_LBLOCK;
+ }
+
+ free(buf);
+ return pad;
+
+} /* _tac_md5_pad */
+
+/* Perform encryption/decryption on buffer. This means simply XORing
+ each byte from buffer with according byte from pseudo-random
+ pad. */
+void _tac_crypt(u_char *buf, HDR *th, int length) {
+ int i;
+ u_char *pad;
+
+ /* null operation if no encryption requested */
+ if((tac_secret != NULL) && (th->encryption == TAC_PLUS_ENCRYPTED_FLAG)) {
+ pad = _tac_md5_pad(length, th);
+
+ for (i=0; i<length; i++) {
+ *(buf+i) ^= pad[i];
+ }
+
+ free(pad);
+ } else {
+ TACSYSLOG((LOG_WARNING, "%s: using no TACACS+ encryption", __func__))
+ }
+} /* _tac_crypt */
diff --git a/libtac/lib/hdr_check.c b/libtac/lib/hdr_check.c
new file mode 100644
index 0000000..fab09d8
--- /dev/null
+++ b/libtac/lib/hdr_check.c
@@ -0,0 +1,50 @@
+/* hdr_check.c - Perform basic sanity checks on received packet.
+ *
+ * Copyright (C) 2010, Pawel Krawczyk <pawel.krawczyk@hush.com> and
+ * Jeroen Nijhof <jeroen@jeroennijhof.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program - see the file COPYING.
+ *
+ * See `CHANGES' file for revision history.
+ */
+
+#include "messages.h"
+#include "libtac.h"
+
+/* Checks given reply header for possible inconsistencies:
+ * 1. reply type other than expected
+ * 2. sequence number other than 2 or 4
+ * 3. session_id different from one sent in request
+ * Returns pointer to error message
+ * or NULL when the header seems to be correct
+ */
+char *_tac_check_header(HDR *th, int type) {
+ if(th->type != type) {
+ TACSYSLOG((LOG_ERR,\
+ "%s: unrelated reply, type %d, expected %d",\
+ __func__, th->type, type))
+ return protocol_err_msg;
+ } else if (1 == (th->seq_no % 2)) {
+ TACSYSLOG((LOG_ERR, "%s: not a reply - seq_no %d not even",\
+ __func__, th->seq_no))
+ return protocol_err_msg;
+ } /* else if(ntohl(th->session_id) != session_id) {
+ TACSYSLOG((LOG_ERR,\
+ "%s: unrelated reply, received session_id %d != sent %d",\
+ __func__, ntohl(th->session_id), session_id))
+ return protocol_err_msg;
+ } */
+
+ return NULL; /* header is ok */
+} /* check header */
diff --git a/libtac/lib/header.c b/libtac/lib/header.c
new file mode 100644
index 0000000..b746a05
--- /dev/null
+++ b/libtac/lib/header.c
@@ -0,0 +1,94 @@
+/* header.c - Create pre-filled header for TACACS+ request.
+ *
+ * Copyright (C) 2010, Pawel Krawczyk <pawel.krawczyk@hush.com> and
+ * Jeroen Nijhof <jeroen@jeroennijhof.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program - see the file COPYING.
+ *
+ * See `CHANGES' file for revision history.
+ */
+
+#include "libtac.h"
+#include "xalloc.h"
+
+#ifdef HAVE_CONFIG_H
+ #include "config.h"
+#endif
+
+#if defined(HAVE_OPENSSL_RAND_H) && defined(HAVE_LIBCRYPTO)
+# include <openssl/rand.h>
+#else
+# include "magic.h"
+#endif
+
+/* Miscellaneous variables that are global, because we need
+ * store their values between different functions and connections.
+ */
+/* Session identifier. */
+int session_id;
+
+/* Encryption flag. */
+int tac_encryption = 0;
+
+/* Pointer to TACACS+ shared secret string. */
+/* note: tac_secret will point to tacplus_server[i].key */
+const char *tac_secret = NULL;
+
+/* TACACS+ shared login string. */
+char tac_login[64]; /* default is PAP */
+
+/* priv_lvl */
+int tac_priv_lvl = TAC_PLUS_PRIV_LVL_MIN;
+
+/* Authentication Method */
+int tac_authen_method = TAC_PLUS_AUTHEN_METH_TACACSPLUS;
+
+/* Service requesting authentication */
+int tac_authen_service = TAC_PLUS_AUTHEN_SVC_PPP;
+
+/* additional runtime flags */
+
+int tac_debug_enable = 0;
+int tac_readtimeout_enable = 0;
+
+/* Returns pre-filled TACACS+ packet header of given type.
+ * 1. you MUST fill th->datalength and th->version
+ * 2. you MAY fill th->encryption
+ * 3. you are responsible for freeing allocated header
+ * By default packet encryption is enabled. The version
+ * field depends on the TACACS+ request type and thus it
+ * cannot be predefined.
+ */
+HDR *_tac_req_header(u_char type, int cont_session) {
+ HDR *th;
+
+ th=(HDR *) tac_xcalloc(1, TAC_PLUS_HDR_SIZE);
+
+ /* preset some packet options in header */
+ th->type=type;
+ th->seq_no=1; /* always 1 for request */
+ th->encryption=TAC_PLUS_ENCRYPTED_FLAG;
+
+ /* make session_id from pseudo-random number */
+ if (!cont_session) {
+#if defined(HAVE_OPENSSL_RAND_H) && defined(HAVE_LIBCRYPTO)
+ RAND_pseudo_bytes((unsigned char *) &session_id, sizeof(session_id));
+#else
+ session_id = tac_magic();
+#endif
+ }
+ th->session_id = htonl(session_id);
+
+ return th;
+}
diff --git a/libtac/lib/magic.c b/libtac/lib/magic.c
new file mode 100644
index 0000000..d42e620
--- /dev/null
+++ b/libtac/lib/magic.c
@@ -0,0 +1,88 @@
+/* magic.c - PPP Magic Number routines.
+ *
+ * Copyright (C) 1989 Carnegie Mellon University.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program - see the file COPYING.
+ *
+ * See `CHANGES' file for revision history.
+ */
+
+#ifdef HAVE_CONFIG_H
+ #include "config.h"
+#endif
+
+/* if OpenSSL library is available this legacy code will not be compiled in */
+#if !defined(HAVE_OPENSSL_RAND_H) && !defined(HAVE_LIBCRYPTO)
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "magic.h"
+
+static int magic_initialised = 0;
+
+/*
+ * magic_init - Initialize the magic number generator.
+ *
+ * Attempts to compute a random number seed which will not repeat.
+ */
+static void
+tac_magic_init()
+{
+ struct stat statbuf;
+ long seed = 0;
+ struct timeval t;
+
+ if (magic_initialised)
+ return;
+
+ // try to initialise seed from urandom
+ if (!lstat("/dev/urandom", &statbuf) && S_ISCHR(statbuf.st_mode)) {
+ int rfd = open("/dev/urandom", O_RDONLY);
+ if(rfd >= 0) {
+ if (read(rfd, &seed, sizeof(seed)) < 0)
+ seed = 0;
+ close(rfd);
+ }
+ }
+
+ // fallback
+ gettimeofday(&t, NULL);
+ seed ^= gethostid() ^ t.tv_sec ^ t.tv_usec ^ getpid();
+
+ // finally seed the PRNG
+ srandom(seed);
+ magic_initialised = 1;
+}
+
+#include <pthread.h>
+/*
+ * magic - Returns the next magic number.
+ */
+u_int32_t
+tac_magic()
+{
+ static pthread_once_t magic_control = PTHREAD_ONCE_INIT;
+
+ pthread_once(&magic_control, &tac_magic_init);
+
+ return (u_int32_t)random();
+}
+
+#endif
diff --git a/libtac/lib/magic.h b/libtac/lib/magic.h
new file mode 100644
index 0000000..b03be94
--- /dev/null
+++ b/libtac/lib/magic.h
@@ -0,0 +1,30 @@
+/* magic.h - PPP Magic Number definitions.
+ *
+ * Copyright (C) 1989 Carnegie Mellon University.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program - see the file COPYING.
+ *
+ * See `CHANGES' file for revision history.
+ */
+
+#ifndef _MAGIC_H
+#define _MAGIC_H
+
+#include "libtac.h"
+
+__BEGIN_DECLS
+u_int32_t tac_magic __P((void)); /* Returns the next magic number */
+__END_DECLS
+
+#endif
diff --git a/libtac/lib/md5.c b/libtac/lib/md5.c
new file mode 100644
index 0000000..fe29f41
--- /dev/null
+++ b/libtac/lib/md5.c
@@ -0,0 +1,273 @@
+/* md5.c - the source code for MD5 routines
+ *
+ * Copyright (C) 1990, RSA Data Security, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program - see the file COPYING.
+ *
+ * See `CHANGES' file for revision history.
+ */
+
+#ifdef HAVE_CONFIG_H
+ #include "config.h"
+#endif
+
+/* if OpenSSL library is available this legacy code will not be compiled in */
+#if !defined(HAVE_OPENSSL_MD5_H) && !defined(HAVE_LIBCRYPTO)
+
+#include <string.h>
+#include "md5.h"
+
+/* forward declaration */
+static void Transform __P((UINT4 *buf, UINT4 *in));
+
+static unsigned char PADDING[64] = {
+ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/* F, G, H and I are basic MD5 functions */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~z)))
+
+/* ROTATE_LEFT rotates x left n bits */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */
+/* Rotation is separate from addition to prevent recomputation */
+#define FF(a, b, c, d, x, s, ac) \
+ {(a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define GG(a, b, c, d, x, s, ac) \
+ {(a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define HH(a, b, c, d, x, s, ac) \
+ {(a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define II(a, b, c, d, x, s, ac) \
+ {(a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+
+#ifdef __STDC__
+#define UL(x) x##U
+#else
+#define UL(x) x
+#endif
+
+/* The routine MD5Init initializes the message-digest context
+ mdContext. All fields are set to zero.
+ */
+void MD5Init (MD5_CTX *mdContext) {
+ mdContext->i[0] = mdContext->i[1] = (UINT4)0;
+
+ /* Load magic initialization constants. */
+ mdContext->buf[0] = (UINT4)0x67452301;
+ mdContext->buf[1] = (UINT4)0xefcdab89;
+ mdContext->buf[2] = (UINT4)0x98badcfe;
+ mdContext->buf[3] = (UINT4)0x10325476;
+}
+
+/* The routine MD5Update updates the message-digest context to
+ account for the presence of each of the characters inBuf[0..inLen-1]
+ in the message whose digest is being computed.
+ */
+void MD5Update ( MD5_CTX *mdContext, unsigned char *inBuf,
+ unsigned int inLen) {
+
+ UINT4 in[16];
+ int mdi;
+ unsigned int i, ii;
+
+ /* compute number of bytes mod 64 */
+ mdi = (int)((mdContext->i[0] >> 3) & 0x3F);
+
+ /* update number of bits */
+ if ((mdContext->i[0] + ((UINT4)inLen << 3)) < mdContext->i[0])
+ mdContext->i[1]++;
+ mdContext->i[0] += ((UINT4)inLen << 3);
+ mdContext->i[1] += ((UINT4)inLen >> 29);
+
+ while (inLen--) {
+ /* add new character to buffer, increment mdi */
+ mdContext->in[mdi++] = *inBuf++;
+
+ /* transform if necessary */
+ if (mdi == 0x40) {
+ for (i = 0, ii = 0; i < 16; i++, ii += 4)
+ in[i] = (((UINT4)mdContext->in[ii+3]) << 24) |
+ (((UINT4)mdContext->in[ii+2]) << 16) |
+ (((UINT4)mdContext->in[ii+1]) << 8) |
+ ((UINT4)mdContext->in[ii]);
+ Transform (mdContext->buf, in);
+ mdi = 0;
+ }
+ }
+}
+
+/* The routine MD5Final terminates the message-digest computation and
+ ends with the desired message digest in mdContext->digest[0...15].
+ */
+void MD5Final (unsigned char hash[], MD5_CTX *mdContext) {
+ UINT4 in[16];
+ int mdi;
+ unsigned int i, ii;
+ unsigned int padLen;
+
+ /* save number of bits */
+ in[14] = mdContext->i[0];
+ in[15] = mdContext->i[1];
+
+ /* compute number of bytes mod 64 */
+ mdi = (int)((mdContext->i[0] >> 3) & 0x3F);
+
+ /* pad out to 56 mod 64 */
+ padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi);
+ MD5Update (mdContext, PADDING, padLen);
+
+ /* append length in bits and transform */
+ for (i = 0, ii = 0; i < 14; i++, ii += 4)
+ in[i] = (((UINT4)mdContext->in[ii+3]) << 24) |
+ (((UINT4)mdContext->in[ii+2]) << 16) |
+ (((UINT4)mdContext->in[ii+1]) << 8) |
+ ((UINT4)mdContext->in[ii]);
+ Transform (mdContext->buf, in);
+
+ /* store buffer in digest */
+ for (i = 0, ii = 0; i < 4; i++, ii += 4) {
+ mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF);
+ mdContext->digest[ii+1] =
+ (unsigned char)((mdContext->buf[i] >> 8) & 0xFF);
+ mdContext->digest[ii+2] =
+ (unsigned char)((mdContext->buf[i] >> 16) & 0xFF);
+ mdContext->digest[ii+3] =
+ (unsigned char)((mdContext->buf[i] >> 24) & 0xFF);
+ }
+ memcpy(hash, mdContext->digest, 16);
+}
+
+/* Basic MD5 step. Transforms buf based on in.
+ */
+static void Transform ( UINT4 *buf, UINT4 *in) {
+ UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3];
+
+ /* Round 1 */
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+ FF ( a, b, c, d, in[ 0], S11, UL(3614090360)); /* 1 */
+ FF ( d, a, b, c, in[ 1], S12, UL(3905402710)); /* 2 */
+ FF ( c, d, a, b, in[ 2], S13, UL( 606105819)); /* 3 */
+ FF ( b, c, d, a, in[ 3], S14, UL(3250441966)); /* 4 */
+ FF ( a, b, c, d, in[ 4], S11, UL(4118548399)); /* 5 */
+ FF ( d, a, b, c, in[ 5], S12, UL(1200080426)); /* 6 */
+ FF ( c, d, a, b, in[ 6], S13, UL(2821735955)); /* 7 */
+ FF ( b, c, d, a, in[ 7], S14, UL(4249261313)); /* 8 */
+ FF ( a, b, c, d, in[ 8], S11, UL(1770035416)); /* 9 */
+ FF ( d, a, b, c, in[ 9], S12, UL(2336552879)); /* 10 */
+ FF ( c, d, a, b, in[10], S13, UL(4294925233)); /* 11 */
+ FF ( b, c, d, a, in[11], S14, UL(2304563134)); /* 12 */
+ FF ( a, b, c, d, in[12], S11, UL(1804603682)); /* 13 */
+ FF ( d, a, b, c, in[13], S12, UL(4254626195)); /* 14 */
+ FF ( c, d, a, b, in[14], S13, UL(2792965006)); /* 15 */
+ FF ( b, c, d, a, in[15], S14, UL(1236535329)); /* 16 */
+
+ /* Round 2 */
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+ GG ( a, b, c, d, in[ 1], S21, UL(4129170786)); /* 17 */
+ GG ( d, a, b, c, in[ 6], S22, UL(3225465664)); /* 18 */
+ GG ( c, d, a, b, in[11], S23, UL( 643717713)); /* 19 */
+ GG ( b, c, d, a, in[ 0], S24, UL(3921069994)); /* 20 */
+ GG ( a, b, c, d, in[ 5], S21, UL(3593408605)); /* 21 */
+ GG ( d, a, b, c, in[10], S22, UL( 38016083)); /* 22 */
+ GG ( c, d, a, b, in[15], S23, UL(3634488961)); /* 23 */
+ GG ( b, c, d, a, in[ 4], S24, UL(3889429448)); /* 24 */
+ GG ( a, b, c, d, in[ 9], S21, UL( 568446438)); /* 25 */
+ GG ( d, a, b, c, in[14], S22, UL(3275163606)); /* 26 */
+ GG ( c, d, a, b, in[ 3], S23, UL(4107603335)); /* 27 */
+ GG ( b, c, d, a, in[ 8], S24, UL(1163531501)); /* 28 */
+ GG ( a, b, c, d, in[13], S21, UL(2850285829)); /* 29 */
+ GG ( d, a, b, c, in[ 2], S22, UL(4243563512)); /* 30 */
+ GG ( c, d, a, b, in[ 7], S23, UL(1735328473)); /* 31 */
+ GG ( b, c, d, a, in[12], S24, UL(2368359562)); /* 32 */
+
+ /* Round 3 */
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+ HH ( a, b, c, d, in[ 5], S31, UL(4294588738)); /* 33 */
+ HH ( d, a, b, c, in[ 8], S32, UL(2272392833)); /* 34 */
+ HH ( c, d, a, b, in[11], S33, UL(1839030562)); /* 35 */
+ HH ( b, c, d, a, in[14], S34, UL(4259657740)); /* 36 */
+ HH ( a, b, c, d, in[ 1], S31, UL(2763975236)); /* 37 */
+ HH ( d, a, b, c, in[ 4], S32, UL(1272893353)); /* 38 */
+ HH ( c, d, a, b, in[ 7], S33, UL(4139469664)); /* 39 */
+ HH ( b, c, d, a, in[10], S34, UL(3200236656)); /* 40 */
+ HH ( a, b, c, d, in[13], S31, UL( 681279174)); /* 41 */
+ HH ( d, a, b, c, in[ 0], S32, UL(3936430074)); /* 42 */
+ HH ( c, d, a, b, in[ 3], S33, UL(3572445317)); /* 43 */
+ HH ( b, c, d, a, in[ 6], S34, UL( 76029189)); /* 44 */
+ HH ( a, b, c, d, in[ 9], S31, UL(3654602809)); /* 45 */
+ HH ( d, a, b, c, in[12], S32, UL(3873151461)); /* 46 */
+ HH ( c, d, a, b, in[15], S33, UL( 530742520)); /* 47 */
+ HH ( b, c, d, a, in[ 2], S34, UL(3299628645)); /* 48 */
+
+ /* Round 4 */
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+ II ( a, b, c, d, in[ 0], S41, UL(4096336452)); /* 49 */
+ II ( d, a, b, c, in[ 7], S42, UL(1126891415)); /* 50 */
+ II ( c, d, a, b, in[14], S43, UL(2878612391)); /* 51 */
+ II ( b, c, d, a, in[ 5], S44, UL(4237533241)); /* 52 */
+ II ( a, b, c, d, in[12], S41, UL(1700485571)); /* 53 */
+ II ( d, a, b, c, in[ 3], S42, UL(2399980690)); /* 54 */
+ II ( c, d, a, b, in[10], S43, UL(4293915773)); /* 55 */
+ II ( b, c, d, a, in[ 1], S44, UL(2240044497)); /* 56 */
+ II ( a, b, c, d, in[ 8], S41, UL(1873313359)); /* 57 */
+ II ( d, a, b, c, in[15], S42, UL(4264355552)); /* 58 */
+ II ( c, d, a, b, in[ 6], S43, UL(2734768916)); /* 59 */
+ II ( b, c, d, a, in[13], S44, UL(1309151649)); /* 60 */
+ II ( a, b, c, d, in[ 4], S41, UL(4149444226)); /* 61 */
+ II ( d, a, b, c, in[11], S42, UL(3174756917)); /* 62 */
+ II ( c, d, a, b, in[ 2], S43, UL( 718787259)); /* 63 */
+ II ( b, c, d, a, in[ 9], S44, UL(3951481745)); /* 64 */
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+#endif
diff --git a/libtac/lib/md5.h b/libtac/lib/md5.h
new file mode 100644
index 0000000..f0289fe
--- /dev/null
+++ b/libtac/lib/md5.h
@@ -0,0 +1,45 @@
+/* md5.h - header file for implementation of MD5
+ *
+ * Copyright (C) 1990, RSA Data Security, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program - see the file COPYING.
+ *
+ * See `CHANGES' file for revision history.
+ */
+
+#ifndef __MD5_INCLUDE__
+
+#include "libtac.h"
+
+/* typedef a 32-bit type */
+typedef unsigned int UINT4;
+
+/* Data structure for MD5 (Message-Digest) computation */
+typedef struct {
+ UINT4 i[2]; /* number of _bits_ handled mod 2^64 */
+ UINT4 buf[4]; /* scratch buffer */
+ unsigned char in[64]; /* input buffer */
+ unsigned char digest[16]; /* actual digest after MD5Final call */
+} MD5_CTX;
+
+__BEGIN_DECLS
+void MD5Init __P((MD5_CTX*));
+void MD5Update __P((MD5_CTX*, unsigned char*, UINT4));
+void MD5Final __P((unsigned char[], MD5_CTX*));
+__END_DECLS
+
+#define MD5_LEN 16
+
+#define __MD5_INCLUDE__
+#endif /* __MD5_INCLUDE__ */
diff --git a/libtac/lib/messages.c b/libtac/lib/messages.c
new file mode 100644
index 0000000..65645f7
--- /dev/null
+++ b/libtac/lib/messages.c
@@ -0,0 +1,31 @@
+/* messages.c - Various messages returned to user.
+ *
+ * Copyright (C) 2010, Pawel Krawczyk <pawel.krawczyk@hush.com> and
+ * Jeroen Nijhof <jeroen@jeroennijhof.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program - see the file COPYING.
+ *
+ * See `CHANGES' file for revision history.
+ */
+
+char *protocol_err_msg = "(Protocol error)";
+char *authen_syserr_msg = "(Authentication system error)";
+char *author_ok_msg = "(Service granted)";
+char *author_fail_msg = "(Service not allowed)";
+char *author_err_msg = "(Service not allowed. Server error)";
+char *author_syserr_msg = "(Authorization system error)";
+char *acct_ok_msg = "(Accounted ok)";
+char *acct_fail_msg = "(Accounting failed)";
+char *acct_err_msg = "(Accounting failed. Server error)";
+char *acct_syserr_msg = "(Accounting system error)";
diff --git a/libtac/lib/messages.h b/libtac/lib/messages.h
new file mode 100644
index 0000000..1561c08
--- /dev/null
+++ b/libtac/lib/messages.h
@@ -0,0 +1,36 @@
+/* messages.h
+ *
+ * Copyright (C) 2010, Pawel Krawczyk <pawel.krawczyk@hush.com> and
+ * Jeroen Nijhof <jeroen@jeroennijhof.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program - see the file COPYING.
+ *
+ * See `CHANGES' file for revision history.
+ */
+
+#ifndef _MESSAGES_H
+#define _MESSAGES_H
+
+extern char *protocol_err_msg;
+extern char *authen_syserr_msg;
+extern char *author_ok_msg;
+extern char *author_fail_msg;
+extern char *author_err_msg;
+extern char *author_syserr_msg;
+extern char *acct_ok_msg;
+extern char *acct_fail_msg;
+extern char *acct_err_msg;
+extern char *acct_syserr_msg;
+
+#endif
diff --git a/libtac/lib/read_wait.c b/libtac/lib/read_wait.c
new file mode 100644
index 0000000..36ea19e
--- /dev/null
+++ b/libtac/lib/read_wait.c
@@ -0,0 +1,136 @@
+/* read_wait.c - Wait for data to read on a fd.
+ *
+ * Copyright (C) 2011, Darren Besler (dbesler@beehive.mb.ca)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program - see the file COPYING.
+ *
+ * See `CHANGES' file for revision history.
+ */
+
+#define _GNU_SOURCE /* for POLLRDHUP */
+
+#include <sys/time.h>
+#include <poll.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+
+#include "libtac.h"
+
+/* FIONREAD support for sun */
+#ifdef sun
+#include <sys/filio.h>
+#endif
+
+static int delta_msecs(struct timeval *newer, struct timeval *older) {
+ time_t deltasecs;
+ suseconds_t deltausecs;
+ struct timeval now;
+
+ if (newer == NULL) {
+ gettimeofday(&now, NULL);
+ newer = &now;
+ }
+
+ deltasecs = newer->tv_sec - older->tv_sec;
+ if ( newer->tv_usec < older->tv_usec ) {
+ deltasecs--;
+ deltausecs = (1000000+newer->tv_usec) - older->tv_usec;
+ } else {
+ deltausecs = newer->tv_usec - older->tv_usec;
+ }
+ return (deltasecs*1000)+(deltausecs/1000);
+}
+
+
+/*
+ * tac_read_wait
+ *
+ * Parms:
+ * fd - open fd to wait on till data avail, or timeout
+ * timeout - maximum time to wait in milliseconds
+ * size - amount of data to wait for
+ * 0 : any amount of data
+ * >0 : amount of data to wait for
+ * timeleft - return of time left from timeout
+ *
+ * Returns:
+ * 0 - data avail, no timeout
+ * -1 - timeout
+ * n - errno
+ */
+
+int tac_read_wait(int fd, int timeout, int size, int *time_left) {
+ int retval = 0;
+ int remaining;
+ struct pollfd fds[1];
+
+ struct timeval start;
+
+ gettimeofday(&start, NULL);
+
+ /* setup for read timeout.
+ * will use poll() as it provides greatest compatibility
+ * vs setsockopt(SO_RCVTIMEO) which isn't supported on Solaris
+ */
+
+ remaining = timeout; /* in msecs */
+
+ fds[0].fd = fd;
+ /*
+ * should probably have a feature test for POLLRDHUP for non-linux.
+ * It's been in linux and glibc for many years
+ */
+ fds[0].events = POLLIN | POLLRDHUP;
+
+ while (remaining > 0) {
+ int rc;
+ int avail = 0;
+ rc = poll(fds, 1, remaining);
+ remaining = timeout - delta_msecs(NULL, &start);
+ if (remaining < 0)
+ remaining = 0;
+ if ( time_left != NULL ) {
+ *time_left = remaining;
+ }
+
+ /* why did poll return */
+ if (rc == 0) { /* Receive timeout */
+ retval = -1;
+ break;
+ }
+
+ if (rc > 0) { /* there is data available */
+ if (size > 0 && /* check for enuf available? */
+ ioctl(fd,FIONREAD,(char*)&avail) == 0 && avail < size) {
+ if(fds[0].revents & POLLRDHUP) {
+ /* other side closed the socket, stop polling */
+ retval = -1;
+ break;
+ }
+ continue; /* not enuf yet, wait for more */
+ } else {
+ break;
+ }
+ }
+
+ if (rc < 0 && errno == EINTR) { /* interrupt */
+ continue;
+ }
+
+ /* all other conditions is an error */
+ retval = errno;
+ break;
+ }
+ return remaining == 0 ? -1 : retval;
+} /* read_wait */
diff --git a/libtac/lib/version.c b/libtac/lib/version.c
new file mode 100644
index 0000000..fb51a4f
--- /dev/null
+++ b/libtac/lib/version.c
@@ -0,0 +1,24 @@
+/* version.c - TACACS+ library version.
+ *
+ * Copyright (C) 2010, Pawel Krawczyk <pawel.krawczyk@hush.com> and
+ * Jeroen Nijhof <jeroen@jeroennijhof.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program - see the file COPYING.
+ *
+ * See `CHANGES' file for revision history.
+ */
+
+int tac_ver_major = 1;
+int tac_ver_minor = 10;
+int tac_ver_patch = 0; /* patchlevel */
diff --git a/libtac/lib/xalloc.c b/libtac/lib/xalloc.c
new file mode 100644
index 0000000..3f52b6e
--- /dev/null
+++ b/libtac/lib/xalloc.c
@@ -0,0 +1,78 @@
+/* xalloc.c - Failsafe memory allocation functions.
+ * Taken from excellent glibc.info ;)
+ *
+ * Copyright (C) 2010, Pawel Krawczyk <pawel.krawczyk@hush.com> and
+ * Jeroen Nijhof <jeroen@jeroennijhof.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program - see the file COPYING.
+ *
+ * See `CHANGES' file for revision history.
+ */
+
+#include "xalloc.h"
+
+void *tac_xcalloc(size_t nmemb, size_t size) {
+ void *val = calloc(nmemb, size);
+ if(val == 0) {
+ TACSYSLOG((LOG_ERR, "%s: calloc(%u,%u) failed", __func__,\
+ (unsigned) nmemb, (unsigned) size))
+ exit(1);
+ }
+ return val;
+}
+
+void *tac_xrealloc(void *ptr, size_t size) {
+ void *val = realloc(ptr, size);
+ if(val == 0) {
+ TACSYSLOG((LOG_ERR, "%s: realloc(%u) failed", __func__, (unsigned) size))
+ exit(1);
+ }
+ return val;
+}
+
+char *tac_xstrdup(const char *s) {
+ char *p;
+ if (s == NULL) return NULL;
+
+ if ( (p = strdup(s)) == NULL ) {
+ TACSYSLOG((LOG_ERR, "%s: strdup(%s) failed: %m", __func__, s))
+ exit(1);
+ }
+ return p;
+}
+
+
+/*
+ safe string copy that aborts when destination buffer is too small
+*/
+char *tac_xstrcpy(char *dst, const char *src, size_t dst_size) {
+ if (dst == NULL) {
+ TACSYSLOG((LOG_ERR, "tac_xstrcpy(): dst == NULL"));
+ abort();
+ }
+ if (src == NULL) {
+ TACSYSLOG((LOG_ERR, "tac_xstrcpy(): src == NULL"));
+ abort();
+ }
+ if (!dst_size)
+ return NULL;
+
+ if (strlen(src) >= dst_size) {
+ TACSYSLOG((LOG_ERR, "tac_xstrcpy(): argument too long, aborting"));
+ abort();
+ }
+
+ return strcpy(dst, src);
+}
+
diff --git a/libtac/lib/xalloc.h b/libtac/lib/xalloc.h
new file mode 100644
index 0000000..bfc1ce4
--- /dev/null
+++ b/libtac/lib/xalloc.h
@@ -0,0 +1,33 @@
+/* xalloc.h
+ *
+ * Copyright (C) 2010, Pawel Krawczyk <pawel.krawczyk@hush.com> and
+ * Jeroen Nijhof <jeroen@jeroennijhof.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program - see the file COPYING.
+ *
+ * See `CHANGES' file for revision history.
+ */
+
+#ifndef _XALLOC_H
+#define _XALLOC_H
+
+#include "libtac.h"
+
+__BEGIN_DECLS
+extern void *tac_xcalloc(size_t nmemb, size_t size);
+extern void *tac_xrealloc(void *ptr, size_t size);
+extern char *tac_xstrdup(const char *s);
+__END_DECLS
+
+#endif