diff --git a/configure.ac b/configure.ac index e34c7694..b29f55fa 100644 --- a/configure.ac +++ b/configure.ac @@ -48,6 +48,7 @@ dnl -------------------------------------------------------------------- dnl Checks for header files. AC_HEADER_STDC AC_CHECK_HEADERS([arpa/inet.h fcntl.h netdb.h netinet/in.h stdlib.h string.h strings.h sys/socket.h sys/time.h ]) +AC_CHECK_HEADERS([stdbool.h limits.h assert.h]) AC_CHECK_HEADERS([syslog.h unistd.h openssl/md5.h openssl/rand.h linux/random.h sys/random.h]) AC_CHECK_HEADER(security/pam_appl.h, [], [AC_MSG_ERROR([PAM libraries missing. Install with "yum install pam-devel" or "apt-get install libpam-dev".])] ) AM_CONDITIONAL(MY_MD5, [test "$ac_cv_header_openssl_md5_h" = "no" ]) diff --git a/libtac/include/libtac.h b/libtac/include/libtac.h index c872ff71..e15356d2 100644 --- a/libtac/include/libtac.h +++ b/libtac/include/libtac.h @@ -42,6 +42,9 @@ extern "C" { #else #include "cdefs.h" #endif +#include +#include +#include #include "tacplus.h" #if defined(DEBUGTAC) && !defined(TACDEBUG) @@ -87,6 +90,7 @@ struct tac_attrib { struct areply { struct tac_attrib *attr; char *msg; + char *data; int status :8; int flags :8; int seq_no :8; @@ -144,6 +148,8 @@ extern int tac_authen_service; extern int tac_debug_enable; extern int tac_readtimeout_enable; +HDR *_tac_req_header(u_char, int); + /* connect.c */ extern int tac_timeout; @@ -152,30 +158,65 @@ int tac_connect_single(const struct addrinfo *, const char *, struct addrinfo *, int); char *tac_ntop(const struct sockaddr *); -int tac_authen_send(int, const char *, const char *, const char *, const char *, - u_char); +/* authen_s.c */ +u_char tac_get_authen_type(const char *); +void tac_authen_send_pkt(const char *, const char *, const char *, + const char *, u_char, u_char **, unsigned *); +int tac_authen_send(int, const char *, const char *, const char *, + const char *, u_char); + +/* authen_r.c */ +int tac_authen_parse(struct areply *, u_char *, unsigned); int tac_authen_read(int, struct areply *); -int tac_cont_send_seq(int, const char *, int); + +/* cont_s.c */ +void tac_cont_send_pkt(const char *, uint8_t, u_char **, unsigned *); +int tac_cont_send_seq(int, const char *, uint8_t); #define tac_cont_send(fd, pass) tac_cont_send_seq((fd), (pass), 3) -HDR *_tac_req_header(u_char, int); + +/* crypt.c */ void _tac_crypt(u_char *, const HDR *); + +/* author_r.c */ +int tac_author_parse(u_char *, unsigned, struct areply *); +int tac_author_read(int, struct areply *); + +/* author_s.c */ +void tac_author_send_pkt(const char *, const char *, const char *, + struct tac_attrib *, u_char **, unsigned *); +int tac_author_send(int, const char *, const char *, const char *, + struct tac_attrib *); + +/* attrib.c */ void tac_add_attrib(struct tac_attrib **, char *, char *); +void tac_add_attrib_pair(struct tac_attrib **, char *, char, char *); void tac_free_attrib(struct tac_attrib **); -char *tac_acct_flag2str(int); -int tac_acct_send(int, int, const char *, char *, char *, struct tac_attrib *); + +/* acct_s.c */ +char *tac_acct_flag2str(u_char); +void tac_acct_send_pkt(u_char, const char *, const char *, const char *, + struct tac_attrib *, u_char **, unsigned *); +int tac_acct_send(int, u_char, const char *, const char *, const char *, + struct tac_attrib *); + +/* acct_r.c */ +int tac_acct_parse(u_char *, unsigned, struct areply *); int tac_acct_read(int, struct areply *); + +/* xalloc.c */ void *xcalloc(size_t, size_t); void *xrealloc(void *, size_t); char *xstrcpy(char *, const char *, size_t); -char *_tac_check_header(HDR *, int); -int tac_author_send(int, const char *, char *, char *, struct tac_attrib *); -int tac_author_read(int, struct areply *); -void tac_add_attrib_pair(struct tac_attrib **, char *, char, char *); -int tac_read_wait(int, int, int, int *); + +/* hdr_check.c */ +char *_tac_check_header(HDR *, uint8_t); /* magic.c */ u_int32_t magic(void); +/* read_wait.c */ +int tac_read_wait(int, int, int, int *); + #ifdef __cplusplus } #endif diff --git a/libtac/include/tacplus.h b/libtac/include/tacplus.h index 90d7c8bf..c7f0c598 100644 --- a/libtac/include/tacplus.h +++ b/libtac/include/tacplus.h @@ -107,6 +107,7 @@ struct authen_cont { u_short user_msg_len; u_short user_data_len; u_char flags; + u_char msg[0]; #define TAC_PLUS_CONTINUE_FLAG_ABORT 0x01 @@ -133,6 +134,7 @@ struct authen_reply { u_short msg_len; u_short data_len; + u_char msg[0]; }; #define TAC_AUTHEN_REPLY_FIXED_FIELDS_SIZE 6 @@ -173,6 +175,7 @@ struct acct { u_char port_len; u_char r_addr_len; u_char arg_cnt; /* the number of cmd args */ + u_char arg_len[0]; }; #define TAC_ACCT_REQ_FIXED_FIELDS_SIZE 9 @@ -201,6 +204,7 @@ struct author { u_char port_len; u_char r_addr_len; u_char arg_cnt; /* the number of args */ + u_char arg_len[0]; }; #define TAC_AUTHOR_REQ_FIXED_FIELDS_SIZE 8 @@ -211,6 +215,7 @@ struct author_reply { u_char arg_cnt; u_short msg_len; u_short data_len; + u_char arg_len[0]; #define TAC_PLUS_AUTHOR_STATUS_PASS_ADD 0x01 #define TAC_PLUS_AUTHOR_STATUS_PASS_REPL 0x02 diff --git a/libtac/lib/acct_r.c b/libtac/lib/acct_r.c index 6d0d04d9..0c10a29b 100644 --- a/libtac/lib/acct_r.c +++ b/libtac/lib/acct_r.c @@ -32,101 +32,56 @@ * LIBTAC_STATUS_PROTOCOL_ERR * >= 0 : server response, see TAC_PLUS_AUTHEN_STATUS_... */ -int tac_acct_read(int fd, struct areply *re) { - HDR th; +int tac_acct_parse(u_char *pkt, unsigned pkt_total, struct areply *re) { + HDR *th = (HDR *)pkt; struct acct_reply *tb = NULL; size_t ulen_from_header, len_from_body; - ssize_t spacket_read; char *msg = NULL; - int timeleft = 0; - re->attr = NULL; /* unused */ - re->msg = NULL; - - if (tac_readtimeout_enable && - tac_read_wait(fd,tac_timeout*1000, TAC_PLUS_HDR_SIZE,&timeleft) < 0 ) { - TACSYSLOG(LOG_ERR,\ - "%s: reply timeout after %u secs", __FUNCTION__, tac_timeout); - re->msg = xstrdup(acct_syserr_msg); - re->status = LIBTAC_STATUS_READ_TIMEOUT; - free(tb); - return re->status; - } - spacket_read = read(fd, &th, TAC_PLUS_HDR_SIZE); - if(spacket_read < TAC_PLUS_HDR_SIZE) { - TACSYSLOG(LOG_ERR,\ - "%s: short reply header, read %zd of %u expected: %m", __FUNCTION__,\ - spacket_read, TAC_PLUS_HDR_SIZE); - re->msg = xstrdup(acct_syserr_msg); - re->status = LIBTAC_STATUS_SHORT_HDR; - free(tb); - return re->status; - } + re->attr = NULL; /* unused */ + re->msg = re->data = NULL; /* check the reply fields in header */ - msg = _tac_check_header(&th, TAC_PLUS_ACCT); + msg = _tac_check_header(th, TAC_PLUS_ACCT); if(msg != NULL) { re->msg = xstrdup(msg); re->status = LIBTAC_STATUS_PROTOCOL_ERR; - free(tb); TACDEBUG(LOG_DEBUG, "%s: exit status=%d, status message \"%s\"",\ __FUNCTION__, re->status, re->msg != NULL ? re->msg : ""); return re->status; } - ulen_from_header = ntohl(th.datalength); - if (ulen_from_header > TAC_PLUS_MAX_PACKET_SIZE) { - TACSYSLOG(LOG_ERR,\ - "%s: length declared in the packet %zu exceeds max allowed packet size %d",\ - __FUNCTION__,\ - ulen_from_header, TAC_PLUS_MAX_PACKET_SIZE); - re->status=LIBTAC_STATUS_SHORT_HDR; - free(tb); - return re->status; - } - tb=(struct acct_reply *) xcalloc(1, ulen_from_header); + ulen_from_header = ntohl(th->datalength); - /* read reply packet body */ - if (tac_readtimeout_enable && - tac_read_wait(fd,timeleft,ulen_from_header,NULL) < 0 ) { - TACSYSLOG(LOG_ERR,\ - "%s: reply timeout after %u secs", __FUNCTION__, tac_timeout); - re->msg = xstrdup(acct_syserr_msg); - re->status = LIBTAC_STATUS_READ_TIMEOUT; - free(tb); - return re->status; - } + tb = (struct acct_reply *)(pkt + TAC_PLUS_HDR_SIZE); - spacket_read = read(fd, tb, ulen_from_header); - if(spacket_read < (ssize_t) ulen_from_header) { + if (pkt_total != ulen_from_header) { TACSYSLOG(LOG_ERR,\ - "%s: short reply body, read %zd of %zu: %m",\ + "%s: short packet, got %u expected %zu: %m",\ __FUNCTION__,\ - spacket_read, ulen_from_header); + pkt_total, TAC_PLUS_HDR_SIZE + ulen_from_header); re->msg = xstrdup(acct_syserr_msg); re->status = LIBTAC_STATUS_SHORT_BODY; - free(tb); return re->status; } /* decrypt the body */ - _tac_crypt((u_char *) tb, &th); + _tac_crypt((u_char *) tb, th); /* 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; + len_from_body = TAC_ACCT_REPLY_FIXED_FIELDS_SIZE + \ + tb->msg_len + tb->data_len; if(ulen_from_header != len_from_body) { TACSYSLOG(LOG_ERR,\ - "%s: inconsistent reply body, incorrect key?",\ - __FUNCTION__); + "%s: inconsistent reply body, header len %zu versus parsed len %zu",\ + __FUNCTION__, ulen_from_header, len_from_body); re->msg = xstrdup(acct_syserr_msg); re->status = LIBTAC_STATUS_PROTOCOL_ERR; - free(tb); return re->status; } @@ -134,16 +89,23 @@ int tac_acct_read(int fd, struct areply *re) { if(tb->msg_len) { msg=(char *) 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'; + msg[tb->msg_len] = '\0'; re->msg = msg; /* Freed by caller */ } + if(tb->data_len) { + msg=(char *) xcalloc(1, tb->data_len+1); + bcopy((u_char *) tb+TAC_ACCT_REPLY_FIXED_FIELDS_SIZE+tb->data_len, + msg, tb->data_len); + msg[tb->data_len] = '\0'; + re->data = msg; /* Freed by caller */ + } + /* server logged our request successfully */ if (tb->status == TAC_PLUS_ACCT_STATUS_SUCCESS) { TACDEBUG(LOG_DEBUG, "%s: accounted ok", __FUNCTION__); if (!re->msg) re->msg = xstrdup(acct_ok_msg); re->status = tb->status; - free(tb); return re->status; } @@ -162,6 +124,94 @@ int tac_acct_read(int fd, struct areply *re) { break; } - free(tb); return re->status; -} +} /* tac_acct_parse */ + +/* + * 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; + size_t ulen_from_header; + ssize_t spacket_read; + int status, timeleft = 0; + + re->attr = NULL; /* unused */ + re->msg = re->data = NULL; + + if (tac_readtimeout_enable && + tac_read_wait(fd, tac_timeout * 1000, TAC_PLUS_HDR_SIZE, &timeleft) < 0 ) { + TACSYSLOG(LOG_ERR,\ + "%s: reply timeout after %u secs", __FUNCTION__, tac_timeout); + re->msg = xstrdup(acct_syserr_msg); + re->status = LIBTAC_STATUS_READ_TIMEOUT; + return re->status; + } + + th = (HDR *)xcalloc(1, TAC_PLUS_HDR_SIZE); + + spacket_read = read(fd, (char *)th, TAC_PLUS_HDR_SIZE); + if(spacket_read < TAC_PLUS_HDR_SIZE) { + TACSYSLOG(LOG_ERR,\ + "%s: short reply header, read %zd of %u expected: %m", __FUNCTION__,\ + ((spacket_read >= 0) ? spacket_read : 0), TAC_PLUS_HDR_SIZE); + re->msg = xstrdup(acct_syserr_msg); + re->status = LIBTAC_STATUS_SHORT_HDR; + free(th); + return re->status; + } + + ulen_from_header = ntohl(th->datalength); + if (ulen_from_header > TAC_PLUS_MAX_PACKET_SIZE) { + TACSYSLOG(LOG_ERR,\ + "%s: length declared in the packet %zu exceeds max allowed packet size %u", __FUNCTION__,\ + ulen_from_header, TAC_PLUS_MAX_PACKET_SIZE); + re->msg = xstrdup(acct_syserr_msg); + re->status = LIBTAC_STATUS_PROTOCOL_ERR; + free(th); + return re->status; + } + + /* now make room for entire contiguous packet */ + th = (HDR *)xrealloc(th, TAC_PLUS_HDR_SIZE + ulen_from_header); + tb = (struct acct_reply *)((u_char *)th + TAC_PLUS_HDR_SIZE); + + /* read reply packet body */ + if (tac_readtimeout_enable && + tac_read_wait(fd, timeleft, ulen_from_header, NULL) < 0 ) { + TACSYSLOG(LOG_ERR,\ + "%s: reply timeout after %u secs", __FUNCTION__, tac_timeout); + re->msg = xstrdup(acct_syserr_msg); + re->status = LIBTAC_STATUS_READ_TIMEOUT; + free(th); + return re->status; + } + + spacket_read = read(fd, (char *)tb, ulen_from_header); + if(spacket_read < 0 || (size_t) spacket_read < ulen_from_header) { + TACSYSLOG(LOG_ERR,\ + "%s: short reply body, read %zd of %zu: %m",\ + __FUNCTION__,\ + ((spacket_read >= 0) ? spacket_read : 0), ulen_from_header); + re->msg = xstrdup(acct_syserr_msg); + re->status = LIBTAC_STATUS_SHORT_BODY; + free(th); + return re->status; + } + + /* now parse remaining packet fields */ + status = tac_acct_parse((u_char *)th, TAC_PLUS_HDR_SIZE + ulen_from_header, re); + + /* all useful data has been copied out */ + free(th); + + return status; +} /* tac_acct_read */ + diff --git a/libtac/lib/acct_s.c b/libtac/lib/acct_s.c index db680672..a59a88d8 100644 --- a/libtac/lib/acct_s.c +++ b/libtac/lib/acct_s.c @@ -22,7 +22,7 @@ #include "libtac.h" #include "xalloc.h" -char *tac_acct_flag2str(int flag) { +char *tac_acct_flag2str(u_char flag) { switch(flag) { case TAC_PLUS_ACCT_FLAG_MORE: return "more"; @@ -37,143 +37,126 @@ char *tac_acct_flag2str(int flag) { } } -/* - * 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) { +/* allocate and format an Accounting Start packet */ +void tac_acct_send_pkt(u_char type, const char *user, const char *tty, + const char *r_addr, struct tac_attrib *attr, + u_char **_pkt, unsigned *_len) { HDR *th; - struct acct tb; - u_char user_len, port_len, r_addr_len; + struct acct *tb; + unsigned 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 */ + unsigned i; /* arg 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; + unsigned pkt_total, pkt_len = 0; TACDEBUG(LOG_DEBUG, "%s: user '%s', tty '%s', rem_addr '%s', encrypt: %s, type: %s", \ __FUNCTION__, 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 *) 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*) xrealloc(pkt, pkt_len); - - /* see comments in author_s.c - pktp=pkt + pkt_len; - pkt_len += sizeof(a->attr_len); - pkt = xrealloc(pkt, pkt_len); - */ - - bcopy(&a->attr_len, pkt + pktl, sizeof(a->attr_len)); - i++; - - a = a->next; + + /* + * precompute the buffer size so we don't need to keep resizing/copying it + */ + user_len = strlen(user); + port_len = strlen(tty); + r_addr_len = strlen(r_addr); + + assert(user_len <= UCHAR_MAX); + assert(port_len <= UCHAR_MAX); + assert(r_addr_len <= UCHAR_MAX); + +#define TAC_ACCT_REQ_FIXED_TOTAL \ + (TAC_PLUS_HDR_SIZE + TAC_ACCT_REQ_FIXED_FIELDS_SIZE) + + pkt_total = TAC_ACCT_REQ_FIXED_TOTAL + user_len + port_len + r_addr_len; + + /* ... add in attributes */ + for (i = 0, a = attr; a; a = a->next, ++i) { + pkt_total += a->attr_len + 1; /* count length byte too */ } + pkt = (u_char *)xcalloc(1, pkt_total); + th = (HDR *)pkt; + + /* tacacs header */ + th->version = TAC_PLUS_VER_0; + th->type = TAC_PLUS_ACCT; + th->seq_no = 1; + th->encryption = tac_encryption ? TAC_PLUS_ENCRYPTED_FLAG : TAC_PLUS_UNENCRYPTED_FLAG; + session_id = magic(); + th->session_id = htonl(session_id); + th->datalength = htonl(pkt_total - TAC_PLUS_HDR_SIZE); + + /* fixed part of tacacs body */ + tb = (struct acct *)(pkt + TAC_PLUS_HDR_SIZE); + tb->flags = type; + tb->authen_method = tac_authen_method; + tb->priv_lvl = tac_priv_lvl; + tb->authen_type = tac_get_authen_type(tac_login); + tb->authen_service = tac_authen_service; + tb->user_len = user_len; + tb->port_len = port_len; + tb->r_addr_len = r_addr_len; + /* 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); + tb->arg_cnt = i; + + pkt_len = TAC_ACCT_REQ_FIXED_TOTAL + i; /* reserve room for lengths */ - /* -#define PUTATTR(data, len) \ - pktp = pkt + pkt_len; \ - pkt_len += len; \ - pkt = xrealloc(pkt, pkt_len); \ - bcopy(data, pktp, len); - */ #define PUTATTR(data, len) \ - pktl = pkt_len; \ - pkt_len += len; \ - pkt = (u_char*) xrealloc(pkt, pkt_len); \ - bcopy(data, pkt + pktl, len); + bcopy(data, pkt + pkt_len, len); \ + pkt_len += len /* fill user and port fields */ - PUTATTR(user, user_len) - PUTATTR(tty, port_len) - PUTATTR(r_addr, r_addr_len) + 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; + for (i = 0, a = attr; a; a = a->next, ++i) { + tb->arg_len[i] = a->attr_len; + PUTATTR(a->attr, a->attr_len); } - /* finished building packet, fill len_from_header in header */ - th->datalength = htonl(pkt_len); - - /* write header */ - w = write(fd, th, TAC_PLUS_HDR_SIZE); + assert(pkt_len == pkt_total); - if(w < TAC_PLUS_HDR_SIZE) { - TACSYSLOG(LOG_ERR, "%s: short write on header, wrote %d of %d: %m",\ - __FUNCTION__, w, TAC_PLUS_HDR_SIZE); - free(pkt); - free(th); - return LIBTAC_STATUS_WRITE_ERR; - } - /* encrypt packet body */ - _tac_crypt(pkt, th); + _tac_crypt((u_char *)tb, th); + + *_pkt = pkt; + *_len = pkt_total; +} /* tac_acct_send_pkt */ - /* 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",\ - __FUNCTION__, w, pkt_len); +/* + * 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, u_char type, const char *user, const char *tty, + const char *r_addr, struct tac_attrib *attr) { + + u_char *pkt = NULL; + unsigned pkt_total = 0; + int w, ret = 0; + + /* generate the packet */ + tac_acct_send_pkt(type, user, tty, r_addr, attr, &pkt, &pkt_total); + + /* write packet */ + w = write(fd, pkt, pkt_total); + + if(w < 0 || (unsigned) w < pkt_total) { + TACSYSLOG(LOG_ERR, "%s: short write of packet, wrote %d of %d: %m",\ + __FUNCTION__, w, pkt_total); ret = LIBTAC_STATUS_WRITE_ERR; } free(pkt); - free(th); TACDEBUG(LOG_DEBUG, "%s: exit status=%d", __FUNCTION__, ret); return ret; -} +} /* tac_acct_send */ + diff --git a/libtac/lib/authen_r.c b/libtac/lib/authen_r.c index e89faa6f..fc40e281 100644 --- a/libtac/lib/authen_r.c +++ b/libtac/lib/authen_r.c @@ -23,10 +23,7 @@ #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 @@ -35,93 +32,54 @@ * LIBTAC_STATUS_PROTOCOL_ERR * >= 0 : server response, see TAC_PLUS_AUTHEN_STATUS_... */ -int tac_authen_read(int fd, struct areply *re) { - HDR th; +int tac_authen_parse(struct areply *re, u_char *pkt, unsigned pkt_total) { + HDR *th = (HDR *)pkt; struct authen_reply *tb = NULL; size_t len_from_header, len_from_body; - ssize_t spacket_read; char *msg = NULL; - int timeleft = 0; - 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) { - TACSYSLOG( - LOG_ERR, "%s: reply timeout after %u secs", __FUNCTION__, tac_timeout); - re->status = LIBTAC_STATUS_READ_TIMEOUT; - free(tb); - return re->status; - } - spacket_read = read(fd, &th, TAC_PLUS_HDR_SIZE); - if (spacket_read < TAC_PLUS_HDR_SIZE) { - TACSYSLOG( - LOG_ERR, "%s: short reply header, read %zd of %d: %m", __FUNCTION__, spacket_read, TAC_PLUS_HDR_SIZE); - re->status = LIBTAC_STATUS_SHORT_HDR; - free(tb); - return re->status; - } + memset(re, 0, sizeof(*re)); /* check the reply fields in header */ - msg = _tac_check_header(&th, TAC_PLUS_AUTHEN); + msg = _tac_check_header(th, TAC_PLUS_AUTHEN); if (msg != NULL) { re->msg = xstrdup(msg); re->status = LIBTAC_STATUS_PROTOCOL_ERR; - free(tb); return re->status; } - re->seq_no = th.seq_no; + 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 %zu exceeds max packet size %d", __FUNCTION__, len_from_header, TAC_PLUS_MAX_PACKET_SIZE); - re->status = LIBTAC_STATUS_PROTOCOL_ERR; - free(tb); - return re->status; - } - tb = (struct authen_reply *) xcalloc(1, len_from_header); + len_from_header = ntohl(th->datalength); - /* read reply packet body */ - if (tac_readtimeout_enable - && tac_read_wait(fd, timeleft, len_from_header, NULL) < 0) { - TACSYSLOG( - LOG_ERR, "%s: reply timeout after %u secs", __FUNCTION__, tac_timeout); - re->msg = xstrdup(authen_syserr_msg); - re->status = LIBTAC_STATUS_READ_TIMEOUT; - free(tb); - return re->status; - } - spacket_read = read(fd, tb, len_from_header); - if (spacket_read < (ssize_t) len_from_header) { + tb = (struct authen_reply *) (pkt + TAC_PLUS_HDR_SIZE); + + if (pkt_total != TAC_PLUS_HDR_SIZE + len_from_header) { TACSYSLOG( - LOG_ERR, "%s: short reply body, read %zd of %zu: %m", __FUNCTION__, spacket_read, len_from_header); + LOG_ERR, "%s: short packet, got %u expected %zu: %m", __FUNCTION__, + pkt_total, TAC_PLUS_HDR_SIZE + len_from_header); re->msg = xstrdup(authen_syserr_msg); re->status = LIBTAC_STATUS_SHORT_BODY; - free(tb); return re->status; } /* decrypt the body */ - _tac_crypt((u_char *) tb, &th); + _tac_crypt((u_char *) tb, th); /* 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; + len_from_body = TAC_AUTHEN_REPLY_FIXED_FIELDS_SIZE + \ + tb->msg_len + tb->data_len; if (len_from_header != len_from_body) { TACSYSLOG( - LOG_ERR, "%s: inconsistent reply body, incorrect key?", __FUNCTION__); + LOG_ERR, "%s: inconsistent reply body, header len %zu versus parsed len %zu", + __FUNCTION__, len_from_header, len_from_body); re->msg = xstrdup(protocol_err_msg); re->status = LIBTAC_STATUS_PROTOCOL_ERR; - free(tb); return re->status; } @@ -130,23 +88,28 @@ int tac_authen_read(int fd, struct areply *re) { if (0 < tb->msg_len) { msg = 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); - + memcpy(msg, tb->msg, tb->msg_len); + msg[tb->msg_len] = '\0'; re->msg = msg; } + if (0 < tb->data_len) { + msg = xcalloc(tb->data_len + 1, sizeof(char)); + /* first byte beyond msg is data */ + memcpy(msg, &tb->msg[tb->msg_len], tb->data_len); + msg[tb->data_len] = '\0'; + re->data = msg; + } + /* server authenticated username and password successfully */ if (re->status == TAC_PLUS_AUTHEN_STATUS_PASS) { TACDEBUG(LOG_DEBUG, "%s: authentication ok", __FUNCTION__); - free(tb); return re->status; } /* server ask for continue packet with password */ if (re->status == TAC_PLUS_AUTHEN_STATUS_GETPASS) { TACDEBUG(LOG_DEBUG, "%s: continue packet with password needed", __FUNCTION__); - free(tb); return re->status; } @@ -155,15 +118,97 @@ int tac_authen_read(int fd, struct areply *re) { 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); + __func__, tb->msg_len, tb->msg); return re->status; } TACDEBUG(LOG_DEBUG, "%s: authentication failed, server reply status=%d", __FUNCTION__, re->status); - free(tb); return re->status; +} /* tac_authen_parse_pkt */ + +/* 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; + size_t len_from_header; + int r, status, timeleft = 0; + + memset(re, 0, sizeof(*re)); + + /* read the reply header */ + if (tac_readtimeout_enable && + tac_read_wait(fd, tac_timeout * 1000, TAC_PLUS_HDR_SIZE, &timeleft) < 0 ) { + TACSYSLOG(LOG_ERR, + "%s: reply timeout after %d secs", __FUNCTION__, tac_timeout); + re->status = LIBTAC_STATUS_READ_TIMEOUT; + return re->status; + } + + th = (HDR *)xcalloc(1, TAC_PLUS_HDR_SIZE); + + r = read(fd, th, TAC_PLUS_HDR_SIZE); + if (r < TAC_PLUS_HDR_SIZE) { + TACSYSLOG(LOG_ERR, + "%s: short reply header, read %d of %u: %m", __FUNCTION__, + ((r >= 0) ? r : 0), TAC_PLUS_HDR_SIZE); + re->status = LIBTAC_STATUS_SHORT_HDR; + free(th); + return re->status; + } + + len_from_header = ntohl(th->datalength); + + if (len_from_header > TAC_PLUS_MAX_PACKET_SIZE) { + TACSYSLOG(LOG_ERR, + "%s: excessively long packet, got %zu bytes", __FUNCTION__, + TAC_PLUS_HDR_SIZE + len_from_header); + status = LIBTAC_STATUS_PROTOCOL_ERR; + free(th); + return status; + } + + /* now make room for entire contiguous packet */ + th = (HDR *)xrealloc(th, TAC_PLUS_HDR_SIZE + len_from_header); + tb = (struct authen_reply *) ((u_char *)th + TAC_PLUS_HDR_SIZE); + + /* read reply packet body */ + if (tac_readtimeout_enable && + tac_read_wait(fd, timeleft, len_from_header, NULL) < 0 ) { + TACSYSLOG(LOG_ERR, + "%s: reply timeout after %d secs", __FUNCTION__, tac_timeout); + status = LIBTAC_STATUS_READ_TIMEOUT; + } + + r = read(fd, (char *)tb, len_from_header); + if (r < 0 || (unsigned) r < len_from_header) { + TACSYSLOG(LOG_ERR, + "%s: short reply body, read %d of %zu: %m", + __FUNCTION__, + ((r >= 0) ? r : 0), len_from_header); + status = LIBTAC_STATUS_SHORT_BODY; + free(th); + return status; + } + + /* now parse remaining packet fields */ + status = tac_authen_parse(re, (u_char *)th, TAC_PLUS_HDR_SIZE + len_from_header); + + /* all useful data has been copied out */ + free(th); + + return status; } /* tac_authen_read */ diff --git a/libtac/lib/authen_s.c b/libtac/lib/authen_s.c index 8c18b779..44582c17 100644 --- a/libtac/lib/authen_s.c +++ b/libtac/lib/authen_s.c @@ -51,6 +51,19 @@ digest_chap(u_char digest[MD5_LBLOCK], uint8_t id, MD5_Final(digest, &mdcontext); } +u_char tac_get_authen_type(const char *login) +{ + if (*login) { + if (!strcmp(login, "chap")) { + return TAC_PLUS_AUTHEN_TYPE_CHAP; + } else if (!strcmp(login, "login")) { + return TAC_PLUS_AUTHEN_TYPE_ASCII; + } + } + /* default to PAP */ + return TAC_PLUS_AUTHEN_TYPE_PAP; +} + /* this function sends a packet do TACACS+ server, asking * for validation of given username and password * @@ -61,36 +74,26 @@ digest_chap(u_char digest[MD5_LBLOCK], uint8_t id, * LIBTAC_STATUS_WRITE_TIMEOUT * LIBTAC_STATUS_ASSEMBLY_ERR */ -int tac_authen_send(int fd, const char *user, const char *pass, const char *tty, - const char *r_addr, u_char action) { +/* allocate and format an Authentication Start packet */ +void tac_authen_send_pkt(const char *user, const char *pass, const char *tty, + const char *r_addr, u_char action, u_char **_pkt, unsigned *_len) { HDR *th; /* TACACS+ packet header */ - struct authen_start tb; /* message body */ - int user_len, pass_len, port_len, chal_len, token_len, bodylength, w; - int r_addr_len; - int pkt_len = 0; - int ret = 0; - char *chal = "1234123412341234"; + struct authen_start *tb; /* message body */ + unsigned user_len, pass_len, port_len, chal_len, r_addr_len, token_len; + const char *chal = "1234123412341234"; char *token = NULL; u_char *pkt = NULL; + unsigned pkt_total, pkt_len = 0; const uint8_t id = 5; - - 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; + uint8_t authen_type; TACDEBUG(LOG_DEBUG, "%s: user '%s', tty '%s', rem_addr '%s', encrypt: %s", __FUNCTION__, user, tty, r_addr, (tac_encryption) ? "yes" : "no"); + authen_type = tac_get_authen_type(tac_login); + /* get size of submitted data */ user_len = strlen(user); chal_len = strlen(chal); @@ -98,7 +101,7 @@ int tac_authen_send(int fd, const char *user, const char *pass, const char *tty, port_len = strlen(tty); r_addr_len = strlen(r_addr); - if (!strcmp(tac_login, "chap")) { + if (authen_type == TAC_PLUS_AUTHEN_TYPE_CHAP) { u_char digest[MD5_LBLOCK]; digest_chap(digest, id, pass, pass_len, chal, chal_len); @@ -113,84 +116,109 @@ int tac_authen_send(int fd, const char *user, const char *pass, const char *tty, 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 = + assert(user_len <= UCHAR_MAX); + assert(port_len <= UCHAR_MAX); + assert(r_addr_len <= UCHAR_MAX); + assert(token_len <= UCHAR_MAX); + +#define TAC_AUTHEN_START_FIXED_TOTAL \ + (TAC_PLUS_HDR_SIZE + TAC_AUTHEN_START_FIXED_FIELDS_SIZE) + + /* + * precompute the buffer size so we don't need to keep resizing/copying it + */ + pkt_total = TAC_AUTHEN_START_FIXED_TOTAL + + user_len + port_len + r_addr_len + token_len; + + pkt = (u_char *)xcalloc(1, pkt_total); + th = (HDR *)pkt; + + /* set some header options */ + if (authen_type == TAC_PLUS_AUTHEN_TYPE_ASCII) { + th->version = TAC_PLUS_VER_0; + } else { + th->version = TAC_PLUS_VER_1; + } + th->type = TAC_PLUS_AUTHEN; + th->seq_no = 1; + th->encryption = + tac_encryption ? + TAC_PLUS_ENCRYPTED_FLAG : TAC_PLUS_UNENCRYPTED_FLAG; + session_id = magic(); + th->session_id = htonl(session_id); + th->datalength = htonl(pkt_total - TAC_PLUS_HDR_SIZE); + + /* fixed part of tacacs body */ + tb = (struct authen_start *)(pkt + TAC_PLUS_HDR_SIZE); + tb->action = TAC_PLUS_AUTHEN_LOGIN; + tb->priv_lvl = tac_priv_lvl; + if (authen_type == TAC_PLUS_AUTHEN_TYPE_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->authen_type = authen_type; } - 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; + 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; + pkt_len = TAC_AUTHEN_START_FIXED_TOTAL; - th->datalength = htonl(bodylength); +#define PUTATTR(data, len) \ + bcopy(data, pkt + pkt_len, len); \ + pkt_len += len - /* 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", __FUNCTION__, w, TAC_PLUS_HDR_SIZE); - free(token); - free(pkt); - free(th); - return LIBTAC_STATUS_WRITE_ERR; - } + /* fill user, port, rem_addr, data fields */ + PUTATTR(user, user_len); + PUTATTR(tty, port_len); + PUTATTR(r_addr, r_addr_len); + PUTATTR(token, token_len); - /* build the packet */ - pkt = (u_char *) xcalloc(1, bodylength + 10); + /* no longer need token */ + free(token); - 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; + /* encrypt packet body */ + _tac_crypt((u_char *)tb, th); - bcopy(token, pkt + pkt_len, token_len); /* password */ - pkt_len += token_len; + *_pkt = pkt; + *_len = pkt_total; +} /* tac_authen_send_pkt */ - /* pkt_len == bodylength ? */ - if (pkt_len != bodylength) { - TACSYSLOG( - LOG_ERR, "%s: bodylength %d != pkt_len %d", __FUNCTION__, bodylength, pkt_len); - free(token); - free(pkt); - free(th); - return LIBTAC_STATUS_ASSEMBLY_ERR; - } +/* 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, const char *pass, const char *tty, + const char *r_addr, u_char action) { + + u_char *pkt = NULL; + unsigned pkt_total = 0; + int w, ret = 0; - /* encrypt the body */ - _tac_crypt(pkt, th); + /* generate the packet */ + tac_authen_send_pkt(user, pass, tty, r_addr, action, &pkt, &pkt_total); - w = write(fd, pkt, pkt_len); - if (w < 0 || w < pkt_len) { + /* we can now write the packet */ + w = write(fd, pkt, pkt_total); + if (w < 0 || (unsigned) w < pkt_total) { TACSYSLOG( - LOG_ERR, "%s: short write on body, wrote %d of %d: %m", __FUNCTION__, w, pkt_len); + LOG_ERR, "%s: short write on packet, wrote %d of %u: %m", __FUNCTION__, w, pkt_total); ret = LIBTAC_STATUS_WRITE_ERR; } - free(token); free(pkt); - free(th); + TACDEBUG(LOG_DEBUG, "%s: exit status=%d", __FUNCTION__, ret); + return ret; } /* tac_authen_send */ diff --git a/libtac/lib/author_r.c b/libtac/lib/author_r.c index 148f7eaa..5a6ea1d9 100644 --- a/libtac/lib/author_r.c +++ b/libtac/lib/author_r.c @@ -23,13 +23,7 @@ #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 @@ -38,83 +32,38 @@ * LIBTAC_STATUS_PROTOCOL_ERR * >= 0 : server response, see TAC_PLUS_AUTHOR_STATUS_... */ -int tac_author_read(int fd, struct areply *re) { - HDR th; +int tac_author_parse(u_char *pkt, unsigned pkt_total, struct areply *re) { + HDR *th = (HDR *)pkt; struct author_reply *tb = NULL; size_t len_from_header, len_from_body; - ssize_t packet_read; - u_char *pktp = NULL; char *msg = NULL; - int timeleft = 0; - re->msg = NULL; unsigned int r = 0; - bzero(re, sizeof(struct areply)); - if (tac_readtimeout_enable - && tac_read_wait(fd, tac_timeout * 1000, TAC_PLUS_HDR_SIZE, - &timeleft) < 0) { - - TACSYSLOG( - LOG_ERR, "%s: reply timeout after %u secs", __FUNCTION__, tac_timeout); - re->msg = xstrdup(author_syserr_msg); - 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) { - TACSYSLOG( - LOG_ERR, "%s: short reply header, read %zd of %d: %m", __FUNCTION__, packet_read, TAC_PLUS_HDR_SIZE); - re->msg = xstrdup(author_syserr_msg); - re->status = LIBTAC_STATUS_SHORT_HDR; - free(tb); - return re->status; - } + bzero(re, sizeof(*re)); /* check header consistency */ - msg = _tac_check_header(&th, TAC_PLUS_AUTHOR); + msg = _tac_check_header(th, TAC_PLUS_AUTHOR); if (msg != NULL) { /* no need to process body if header is broken */ re->msg = 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 %zu exceeds max packet size %d", __FUNCTION__, len_from_header, TAC_PLUS_MAX_PACKET_SIZE); - re->status = LIBTAC_STATUS_PROTOCOL_ERR; - free(tb); - return re->status; - } - tb = (struct author_reply *) xcalloc(1, len_from_header); + len_from_header = ntohl(th->datalength); - /* read reply packet body */ - if (tac_readtimeout_enable - && tac_read_wait(fd, timeleft, len_from_header, NULL) < 0) { + tb = (struct author_reply *)(pkt + TAC_PLUS_HDR_SIZE); + if (pkt_total != TAC_PLUS_HDR_SIZE + len_from_header) { TACSYSLOG( - LOG_ERR, "%s: reply timeout after %u secs", __FUNCTION__, tac_timeout); - re->msg = xstrdup(author_syserr_msg); - re->status = LIBTAC_STATUS_READ_TIMEOUT; - free(tb); - return re->status; - } - packet_read = read(fd, tb, len_from_header); - if (packet_read < (ssize_t) len_from_header) { - TACSYSLOG( - LOG_ERR, "%s: short reply body, read %zd of %zu", __FUNCTION__, packet_read, len_from_header); + LOG_ERR, "%s: short packet, got %u of %zu", __FUNCTION__, pkt_total, len_from_header); re->msg = xstrdup(author_syserr_msg); re->status = LIBTAC_STATUS_SHORT_BODY; - free(tb); return re->status; } /* decrypt the body */ - _tac_crypt((u_char *) tb, &th); + _tac_crypt((u_char *) tb, th); /* Convert network byte order to host byte order */ tb->msg_len = ntohs(tb->msg_len); @@ -127,42 +76,39 @@ int tac_author_read(int fd, struct areply *re) { 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 < TAC_PLUS_MAX_ARGCOUNT; - r++) { - if ((ssize_t) len_from_body > packet_read - || ((void *) pktp - (void *) tb) > packet_read) { + for (r = 0; ; r++) { + if (len_from_body > pkt_total) { TACSYSLOG( LOG_ERR, "%s: arguments supplied in packet seem to exceed its size", __FUNCTION__); re->msg = 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 (r == tb->arg_cnt) + break; + + len_from_body += sizeof(tb->arg_len[0]) + tb->arg_len[r]; } if (len_from_header != len_from_body) { TACSYSLOG( - LOG_ERR, "%s: inconsistent reply body, incorrect key?", __FUNCTION__); + LOG_ERR, "%s: inconsistent reply body, header len %zu versus parsed len %zu", __FUNCTION__, len_from_header, len_from_body); re->msg = 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 *) 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'; + + (tb->arg_cnt) * sizeof(tb->arg_len[0]), msg, tb->msg_len); + msg[tb->msg_len] = '\0'; re->msg = msg; /* freed by caller */ } @@ -171,11 +117,11 @@ int tac_author_read(int fd, struct areply *re) { char *smsg = (char *) 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->arg_cnt) * sizeof(tb->arg_len[0]) + tb->msg_len, smsg, tb->data_len); - smsg[(int) tb->data_len] = '\0'; + smsg[tb->data_len] = '\0'; + re->data = smsg; /* Freed by caller */ TACSYSLOG(LOG_ERR, "%s: reply message: %s", __FUNCTION__, smsg); - free(smsg); } TACDEBUG(LOG_DEBUG, "%s: authorization reply status=%d", @@ -186,6 +132,9 @@ int tac_author_read(int fd, struct areply *re) { /* success conditions */ /* XXX support optional vs mandatory arguments */ case TAC_PLUS_AUTHOR_STATUS_PASS_REPL: + /* @@@ we bzero'd the pointer at the top of this function, + * so there's nothing left to free here! + */ tac_free_attrib(&re->attr); case TAC_PLUS_AUTHOR_STATUS_PASS_ADD: { @@ -197,10 +146,11 @@ int tac_author_read(int fd, struct areply *re) { /* 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; + argp = (u_char *) tb + TAC_AUTHOR_REPLY_FIXED_FIELDS_SIZE + + (tb->arg_cnt * sizeof(tb->arg_len[0])) + tb->msg_len + + tb->data_len; TACSYSLOG(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 < TAC_PLUS_MAX_ARGCOUNT; @@ -210,12 +160,15 @@ int tac_author_read(int fd, struct areply *re) { char *value; char sepchar = '='; - bcopy(argp, buff, *pktp); - buff[*pktp] = '\0'; - sep = strchr(buff, '='); + bcopy(argp, buff, tb->arg_len[r]); + buff[tb->arg_len[r]] = '\0'; + + sep = strchr(buff, sepchar); + 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); @@ -227,20 +180,17 @@ int tac_author_read(int fd, struct areply *re) { *sep = '\0'; value = ++sep; /* now buff points to attribute name, - value to the attribute value */ + value to the attribute value (only + buff needs to be freed). */ } TACSYSLOG(LOG_DEBUG, "Adding buf/value pair (%s,%s)", buff, value); tac_add_attrib_pair(&re->attr, buff, sepchar, value); - argp += *pktp; - pktp++; + argp += tb->arg_len[r]; } - } - 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: @@ -255,8 +205,101 @@ int tac_author_read(int fd, struct areply *re) { if (!re->msg) re->msg = xstrdup(author_err_msg); re->status = TAC_PLUS_AUTHOR_STATUS_ERROR; + break; } - free(tb); return re->status; } + +/* 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; + size_t len_from_header; + ssize_t packet_read; + int timeleft = 0; + + bzero(re, sizeof(*re)); + + if (tac_readtimeout_enable + && tac_read_wait(fd, tac_timeout * 1000, TAC_PLUS_HDR_SIZE, + &timeleft) < 0) { + + TACSYSLOG( + LOG_ERR, "%s: reply timeout after %d secs", __FUNCTION__, tac_timeout); + re->msg = xstrdup(author_syserr_msg); + re->status = LIBTAC_STATUS_READ_TIMEOUT; + return re->status; + } + + th = (HDR *)xcalloc(1, TAC_PLUS_HDR_SIZE); + + packet_read = read(fd, th, TAC_PLUS_HDR_SIZE); + if (packet_read < TAC_PLUS_HDR_SIZE) { + TACSYSLOG( + LOG_ERR, "%s: short reply header, read %zd of %u: %m", __FUNCTION__, + ((packet_read >= 0) ? packet_read : 0), TAC_PLUS_HDR_SIZE); + re->msg = xstrdup(author_syserr_msg); + re->status = LIBTAC_STATUS_SHORT_HDR; + free(th); + 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 %zu exceeds max packet size %d", __FUNCTION__, len_from_header, TAC_PLUS_MAX_PACKET_SIZE); + re->msg = xstrdup(author_syserr_msg); + re->status = LIBTAC_STATUS_PROTOCOL_ERR; + free(th); + return re->status; + } + + /* now make room for entire contiguous packet */ + th = (HDR *)xrealloc(th, TAC_PLUS_HDR_SIZE + len_from_header); + tb = (struct author_reply *)((u_char *)th + TAC_PLUS_HDR_SIZE); + + /* read reply packet body */ + if (tac_readtimeout_enable + && tac_read_wait(fd, timeleft, len_from_header, NULL) < 0) { + TACSYSLOG( + LOG_ERR, "%s: reply timeout after %u secs", __FUNCTION__, tac_timeout); + re->msg = xstrdup(author_syserr_msg); + re->status = LIBTAC_STATUS_READ_TIMEOUT; + free(th); + return re->status; + } + packet_read = read(fd, tb, len_from_header); + if (packet_read < 0 || (size_t) packet_read < len_from_header) { + TACSYSLOG( + LOG_ERR, "%s: short reply body, read %zd of %zu: %m", __FUNCTION__, ((packet_read >= 0) ? packet_read : 0), len_from_header); + re->msg = xstrdup(author_syserr_msg); + re->status = LIBTAC_STATUS_SHORT_BODY; + free(th); + return re->status; + } + + /* now parse remaining packet fields */ + (void) tac_author_parse((u_char *)th, TAC_PLUS_HDR_SIZE + len_from_header, + re); + + /* all useful data has been copied out */ + free(th); + + return re->status; +} /* tac_author_read */ + diff --git a/libtac/lib/author_s.c b/libtac/lib/author_s.c index d067e2c0..0a49b8ff 100644 --- a/libtac/lib/author_s.c +++ b/libtac/lib/author_s.c @@ -22,147 +22,124 @@ #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) { +/* allocate and format an Authorization Start packet */ +void tac_author_send_pkt(const char *user, const char *tty, const char *r_addr, + struct tac_attrib *attr, u_char **_pkt, unsigned *_len) { HDR *th; - struct author tb; - u_char user_len, port_len, r_addr_len; + struct author *tb; + unsigned 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 */ + unsigned i; /* attributes count */ 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; + unsigned pkt_total, pkt_len = 0; TACDEBUG(LOG_DEBUG, "%s: user '%s', tty '%s', rem_addr '%s', encrypt: %s", __FUNCTION__, user, tty, r_addr, tac_encryption ? "yes" : "no"); + /* + * precompute the buffer size so we don't need to keep resizing/copying it + */ 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 *) 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*) xrealloc(pkt, pkt_len); - - /* bad method: realloc() is allowed to return different pointer - with each call - pktp=pkt + pkt_len; - pkt_len += sizeof(a->attr_len); - pkt = xrealloc(pkt, pkt_len); - */ - - bcopy(&a->attr_len, pkt + pktl, sizeof(a->attr_len)); - i++; - - a = a->next; + assert(user_len <= UCHAR_MAX); + assert(port_len <= UCHAR_MAX); + assert(r_addr_len <= UCHAR_MAX); + +#define TAC_AUTHOR_REQ_FIXED_TOTAL \ + (TAC_PLUS_HDR_SIZE + TAC_AUTHOR_REQ_FIXED_FIELDS_SIZE) + + pkt_total = TAC_AUTHOR_REQ_FIXED_TOTAL + user_len + port_len + r_addr_len; + + /* ... add in attributes */ + for (i = 0, a = attr; a; a = a->next, ++i) { + pkt_total += a->attr_len + 1; /* count length byte too */ } + pkt = (u_char *)xcalloc(1, pkt_total); + th = (HDR *)pkt; + + /* tacacs header */ + th->version = TAC_PLUS_VER_0; + th->type = TAC_PLUS_AUTHOR; + th->seq_no = 1; + th->encryption = tac_encryption ? TAC_PLUS_ENCRYPTED_FLAG : TAC_PLUS_UNENCRYPTED_FLAG; + session_id = magic(); + th->session_id = htonl(session_id); + th->datalength = htonl(pkt_total - TAC_PLUS_HDR_SIZE); + + /* fixed part of tacacs body */ + tb = (struct author *)(pkt + TAC_PLUS_HDR_SIZE); + tb->authen_method = tac_authen_method; + tb->priv_lvl = tac_priv_lvl; + tb->authen_type = tac_get_authen_type(tac_login); + tb->service = tac_authen_service; + tb->user_len = user_len; + tb->port_len = port_len; + tb->r_addr_len = r_addr_len; + /* 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) \ - pktp = pkt + pkt_len; \ - pkt_len += len; \ - pkt = xrealloc(pkt, pkt_len); \ - bcopy(data, pktp, len); - */ + tb->arg_cnt = i; + + pkt_len = TAC_AUTHOR_REQ_FIXED_TOTAL + i; /* reserve room for lengths */ #define PUTATTR(data, len) \ - pktl = pkt_len; \ - pkt_len += len; \ - pkt = (u_char*) xrealloc(pkt, pkt_len); \ - bcopy(data, pkt + pktl, len); + bcopy(data, pkt + pkt_len, len); \ + pkt_len += len /* fill user and port fields */ - PUTATTR(user, user_len) - PUTATTR(tty, port_len) - PUTATTR(r_addr, r_addr_len) + 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; + for (i = 0, a = attr; a; a = a->next, i++) { + tb->arg_len[i] = a->attr_len; + PUTATTR(a->attr, a->attr_len); } - /* finished building packet, fill len_from_header in header */ - th->datalength = htonl(pkt_len); + assert(pkt_len == pkt_total); - /* write header */ - w = write(fd, th, TAC_PLUS_HDR_SIZE); + /* encrypt packet body */ + _tac_crypt((u_char *)tb, th); - if (w < TAC_PLUS_HDR_SIZE) { - TACSYSLOG( - LOG_ERR, "%s: short write on header, wrote %d of %d: %m", __FUNCTION__, w, TAC_PLUS_HDR_SIZE); - free(pkt); - free(th); - return LIBTAC_STATUS_WRITE_ERR; - } + *_pkt = pkt; + *_len = pkt_total; +} /* tac_author_send_pkt */ - /* encrypt packet body */ - _tac_crypt(pkt, th); +/* 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, const char *tty, const char *r_addr, + struct tac_attrib *attr) { + + u_char *pkt = NULL; + unsigned pkt_total = 0; + int w, ret = 0; - /* 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", __FUNCTION__, w, pkt_len); + /* generate the packet */ + tac_author_send_pkt(user, tty, r_addr, attr, &pkt, &pkt_total); + + /* write packet */ + w = write(fd, pkt, pkt_total); + if (w < 0 || (unsigned) w < pkt_total) { + TACSYSLOG(LOG_ERR, "%s: short write on packet, wrote %d of %d: %m",\ + __FUNCTION__, w, pkt_total); ret = LIBTAC_STATUS_WRITE_ERR; } free(pkt); - free(th); TACDEBUG(LOG_DEBUG, "%s: exit status=%d", __FUNCTION__, ret); return ret; -} +} /* tac_author_send */ + diff --git a/libtac/lib/cont_s.c b/libtac/lib/cont_s.c index e2815672..92aa650b 100644 --- a/libtac/lib/cont_s.c +++ b/libtac/lib/cont_s.c @@ -25,84 +25,99 @@ # include "md5.h" #endif -/* 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, const char *pass, int seq) { +/* allocate and format an continue packet */ +void tac_cont_send_pkt(const char *pass, uint8_t seq, + u_char **_pkt, unsigned *_len) { + HDR *th; /* TACACS+ packet header */ - struct authen_cont tb; /* continue body */ - int pass_len, bodylength, w; - int pkt_len = 0; - int ret = 0; + struct authen_cont *tb; /* continue body */ + unsigned pass_len; u_char *pkt = NULL; + unsigned pkt_total, pkt_len = 0; - th = _tac_req_header(TAC_PLUS_AUTHEN, 1); + /* get size of submitted data */ + pass_len = strlen(pass); + + assert(pass_len <= UCHAR_MAX); + + /* @@@ really need to make tac_secret, tac_encryption, session_id + * be bound to the session... */ + +#define TAC_AUTHEN_CONT_FIXED_TOTAL \ + (TAC_PLUS_HDR_SIZE + TAC_AUTHEN_CONT_FIXED_FIELDS_SIZE) + + /* + * precompute the buffer size so we don't need to keep resizing/copying it + */ + pkt_total = TAC_AUTHEN_CONT_FIXED_TOTAL + pass_len; + + /* build the packet */ + pkt = (u_char *)xcalloc(1, pkt_total); + th = (HDR *)pkt; /* set some header options */ th->version = TAC_PLUS_VER_0; + th->type = TAC_PLUS_AUTHEN; th->seq_no = seq; /* 1 = request, 2 = reply, 3 = continue, 4 = reply */ th->encryption = tac_encryption ? TAC_PLUS_ENCRYPTED_FLAG : TAC_PLUS_UNENCRYPTED_FLAG; + th->session_id = htonl(session_id); + th->datalength = htonl(pkt_total - TAC_PLUS_HDR_SIZE); - /* get size of submitted data */ - pass_len = strlen(pass); + /* fixed part of tacacs body */ + tb = (struct authen_cont *)(pkt + TAC_PLUS_HDR_SIZE); + tb->flags = 0; + tb->user_msg_len = htons(pass_len); + tb->user_data_len = 0; - /* fill the body of message */ - tb.user_msg_len = htons(pass_len); - tb.user_data_len = tb.flags = 0; + pkt_len = TAC_AUTHEN_CONT_FIXED_TOTAL; /* reserve room for lengths */ - /* fill body length in header */ - bodylength = TAC_AUTHEN_CONT_FIXED_FIELDS_SIZE + 0 + pass_len; +#define PUTATTR(data, len) \ + bcopy(data, pkt + pkt_len, len); \ + pkt_len += len - th->datalength = htonl(bodylength); + PUTATTR(pass, pass_len); - /* 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", __FUNCTION__, w, TAC_PLUS_HDR_SIZE); - free(pkt); - free(th); - return LIBTAC_STATUS_WRITE_ERR; - } + assert(pkt_len == pkt_total); - /* build the packet */ - pkt = (u_char *) xcalloc(1, bodylength); + /* encrypt the body */ + _tac_crypt((u_char *)tb, th); - 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 = pkt; + *_len = pkt_total; +} /* tac_cont_send */ - /* pkt_len == bodylength ? */ - if (pkt_len != bodylength) { - TACSYSLOG( - LOG_ERR, "%s: bodylength %d != pkt_len %d", __FUNCTION__, bodylength, pkt_len); - free(pkt); - free(th); - return LIBTAC_STATUS_ASSEMBLY_ERR; - } +/* 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, const char *pass, uint8_t seq) { - /* encrypt the body */ - _tac_crypt(pkt, th); + u_char *pkt = NULL; + unsigned pkt_total = 0; + int w, ret = 0; + + /* generate the packet */ + tac_cont_send_pkt(pass, seq, &pkt, &pkt_total); - w = write(fd, pkt, pkt_len); - if (w < 0 || w < pkt_len) { + w = write(fd, pkt, pkt_total); + if (w < 0 || (unsigned) w < pkt_total) { TACSYSLOG( - LOG_ERR, "%s: short write on body, wrote %d of %d: %m", __FUNCTION__, w, pkt_len); + LOG_ERR, "%s: short write on packet, wrote %d of %u: %m", __FUNCTION__, w, pkt_total); ret = LIBTAC_STATUS_WRITE_ERR; } free(pkt); - free(th); + TACDEBUG(LOG_DEBUG, "%s: exit status=%d", __FUNCTION__, ret); + return ret; } /* tac_cont_send */ + diff --git a/libtac/lib/hdr_check.c b/libtac/lib/hdr_check.c index 36866ff8..724e02b2 100644 --- a/libtac/lib/hdr_check.c +++ b/libtac/lib/hdr_check.c @@ -29,7 +29,7 @@ * Returns pointer to error message * or NULL when the header seems to be correct */ -char *_tac_check_header(HDR *th, int type) { +char *_tac_check_header(HDR *th, uint8_t type) { if(th->type != type) { TACSYSLOG(LOG_ERR,\ "%s: unrelated reply, type %d, expected %d",\