diff options
author | Christian Poessinger <christian@poessinger.com> | 2021-05-02 19:07:04 +0200 |
---|---|---|
committer | Christian Poessinger <christian@poessinger.com> | 2021-05-02 19:18:42 +0200 |
commit | ceb7d3cb30a23b4b148bc71491b3817e9e6e2778 (patch) | |
tree | 160ebe6294acb6a790790098b3861e58c0ca9ae4 /libtac/lib | |
download | libpam-tacplus-ceb7d3cb30a23b4b148bc71491b3817e9e6e2778.tar.gz libpam-tacplus-ceb7d3cb30a23b4b148bc71491b3817e9e6e2778.zip |
Initial import of libpam-tacplus (1.4.3-cl3u4)
Diffstat (limited to 'libtac/lib')
-rw-r--r-- | libtac/lib/acct_r.c | 190 | ||||
-rw-r--r-- | libtac/lib/acct_s.c | 179 | ||||
-rw-r--r-- | libtac/lib/attrib.c | 99 | ||||
-rw-r--r-- | libtac/lib/authen_r.c | 197 | ||||
-rw-r--r-- | libtac/lib/authen_s.c | 190 | ||||
-rw-r--r-- | libtac/lib/author_r.c | 283 | ||||
-rw-r--r-- | libtac/lib/author_s.c | 154 | ||||
-rw-r--r-- | libtac/lib/connect.c | 236 | ||||
-rw-r--r-- | libtac/lib/cont_s.c | 104 | ||||
-rw-r--r-- | libtac/lib/crypt.c | 114 | ||||
-rw-r--r-- | libtac/lib/hdr_check.c | 50 | ||||
-rw-r--r-- | libtac/lib/header.c | 94 | ||||
-rw-r--r-- | libtac/lib/magic.c | 88 | ||||
-rw-r--r-- | libtac/lib/magic.h | 30 | ||||
-rw-r--r-- | libtac/lib/md5.c | 273 | ||||
-rw-r--r-- | libtac/lib/md5.h | 45 | ||||
-rw-r--r-- | libtac/lib/messages.c | 31 | ||||
-rw-r--r-- | libtac/lib/messages.h | 36 | ||||
-rw-r--r-- | libtac/lib/read_wait.c | 136 | ||||
-rw-r--r-- | libtac/lib/version.c | 24 | ||||
-rw-r--r-- | libtac/lib/xalloc.c | 78 | ||||
-rw-r--r-- | libtac/lib/xalloc.h | 33 |
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 |