/* 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, but actually the aren't. Attributes are discarded. */ void tac_author_read(int fd, struct areply *re) { HDR th; struct author_reply *tb; int len_from_header, r, len_from_body; char *pktp; char *msg = NULL; bzero(re, sizeof(struct areply)); r=read(fd, &th, TAC_PLUS_HDR_SIZE); if(r < TAC_PLUS_HDR_SIZE) { syslog(LOG_ERR, "%s: short author header, %d of %d: %m", __FUNCTION__, r, TAC_PLUS_HDR_SIZE); re->msg = system_err_msg; re->status = AUTHOR_STATUS_ERROR; goto AuthorExit; } /* 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 = msg; re->status = AUTHOR_STATUS_ERROR; goto AuthorExit; } len_from_header=ntohl(th.datalength); tb=(struct author_reply *) xcalloc(1, len_from_header); /* read reply packet body */ r=read(fd, tb, len_from_header); if(r < len_from_header) { syslog(LOG_ERR, "%s: short author body, %d of %d: %m", __FUNCTION__, r, len_from_header); re->msg = system_err_msg; re->status = AUTHOR_STATUS_ERROR; goto AuthorExit; } /* decrypt the body */ _tac_crypt((u_char *) tb, &th, len_from_header); /* 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; for(r = 0; r < tb->arg_cnt; r++) { 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) { syslog(LOG_ERR, "%s: inconsistent author reply body, incorrect key?", __FUNCTION__); re->msg = system_err_msg; re->status = AUTHOR_STATUS_ERROR; goto AuthorExit; } /* 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); re->msg = msg; } /* server message to syslog */ if(tb->data_len) { 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->data_len); syslog(LOG_ERR, "%s: author failed: %s", __FUNCTION__,smsg); free(smsg); } /* prepare status */ switch(tb->status) { /* success conditions */ /* XXX support optional vs mandatory arguments */ case AUTHOR_STATUS_PASS_ADD: case AUTHOR_STATUS_PASS_REPL: { char *argp; if(!re->msg) re->msg=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; /* argp points to current argument string pktp holds current argument length */ for(r=0; r < tb->arg_cnt; r++) { char buff[256]; char name[128]; char content[128]; char *sep; char *attrib; char *value; bcopy(argp, buff, *pktp); buff[*pktp] = '\0'; sep=index(buff, '='); if(sep == NULL) index(buff, '*'); if(sep == NULL) syslog(LOG_WARNING, "%s: attribute contains no separator: %s", buff); *sep = '\0'; value = ++sep; /* now buff points to attribute name, value to the attribute value */ tac_add_attrib(&re->attr, buff, value); argp += *pktp; pktp++; } } break; /* authorization failure conditions */ /* failing to follow is allowed by RFC, page 23 */ case AUTHOR_STATUS_FOLLOW: case AUTHOR_STATUS_FAIL: if(!re->msg) re->msg=author_fail_msg; re->status=AUTHOR_STATUS_FAIL; break; /* error conditions */ case AUTHOR_STATUS_ERROR: default: if(!re->msg) re->msg=author_err_msg; re->status=AUTHOR_STATUS_ERROR; } AuthorExit: free(tb); TACDEBUG((LOG_DEBUG, "%s: server replied '%s'", __FUNCTION__, \ re->msg)) }
const char *tac_account_read(int fd) { HDR th; struct acct_reply *tb; int len_from_header, r, len_from_body; const char *msg = NULL; char * msg_tmp = NULL; r=read(fd, &th, TAC_PLUS_HDR_SIZE); if(r < TAC_PLUS_HDR_SIZE) { DEBUG_TACPLUS("short acct header, %d of %d", r, TAC_PLUS_HDR_SIZE); return(system_err_msg); } /* check the reply fields in header */ msg = _tac_check_header(&th, TAC_PLUS_ACCT); if(msg != NULL) return(msg); len_from_header=ntohl(th.datalength); tb=(struct acct_reply *) xcalloc(1, len_from_header); /* read reply packet body */ r=read(fd, tb, len_from_header); if(r < len_from_header) { DEBUG_TACPLUS("incomplete message body, %d bytes, expected %d", r, len_from_header); return(system_err_msg); } /* 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) { DEBUG_TACPLUS("invalid reply content, incorrect key?"); return(system_err_msg); } /* save status and clean up */ r=tb->status; if(tb->msg_len) { msg_tmp = (char *) xcalloc(1, tb->msg_len); bcopy((u_char *) tb+TAC_ACCT_REPLY_FIXED_FIELDS_SIZE, msg_tmp, tb->msg_len); msg = msg_tmp; } else msg="Accounting failed"; free(tb); /* server logged our request successfully */ if(r == TAC_PLUS_ACCT_STATUS_SUCCESS) { DEBUG_TACPLUS("accounted ok"); msg=NULL; return(NULL); } /* return pointer to server message */ DEBUG_TACPLUS("accounting failed, server reply was %d (%s)", r, msg); return(msg); }
int tac_account_read(tacacs_conf_t *conf, tacacs_server_t *server, const char **ret_err_msg) { int err = 0; HDR th; struct acct_reply *tb = NULL; int len_from_header, r, len_from_body; const char *msg = NULL; int msg_len, data_len; if (ret_err_msg == NULL) { syslog(LOG_ERR, "%s: error no err msg buffer provided", __FUNCTION__); err = -1; goto bail; } *ret_err_msg = NULL; if (server == NULL) { syslog(LOG_ERR, "%s: error no TACACS+ server specified", __FUNCTION__); err = -1; *ret_err_msg = no_server_err_msg; goto bail; } reset_server(server, FALSE); r=read(conf->sockfd, &th, TAC_PLUS_HDR_SIZE); if(r < TAC_PLUS_HDR_SIZE) { syslog(LOG_ERR, "%s: short acct header, %d of %d: %m", __FUNCTION__, r, TAC_PLUS_HDR_SIZE); err = -1; *ret_err_msg = r == 0 ? "No response from server" : "Truncated response from server"; goto bail; } /* check the reply fields in header */ err = _tac_check_header(&th, TAC_PLUS_ACCT, server, &msg); if (err) { *ret_err_msg = msg; goto bail; } len_from_header=ntohl(th.datalength); tb=(struct acct_reply *) xcalloc(1, len_from_header); /* read reply packet body */ r=read(conf->sockfd, tb, len_from_header); if(r < len_from_header) { syslog(LOG_ERR, "%s: incomplete message body, %d bytes, expected %d: %m", __FUNCTION__, r, len_from_header); err = -1; *ret_err_msg = r == 0 ? "Empty response message from server" : "Incomplete response message from server"; goto bail; } /* decrypt the body */ _tac_crypt(server->secret, (u_char *) tb, &th, len_from_header); msg_len = tb->msg_len; data_len = tb->data_len; /* check the length fields */ len_from_body=sizeof(tb->msg_len) + sizeof(tb->data_len) + sizeof(tb->status) + msg_len + data_len; if(len_from_header != len_from_body) { syslog(LOG_ERR, "%s: invalid reply content, incorrect key?", __FUNCTION__); err = -1; *ret_err_msg = system_err_msg; goto bail; } /* save status and clean up */ r=tb->status; if(msg_len) { server->server_msg=(char *) xcalloc(1, msg_len); bcopy(tb+TAC_ACCT_REPLY_FIXED_FIELDS_SIZE, server->server_msg, msg_len); *ret_err_msg = server->server_msg; } /* server logged our request successfully */ if(r == TAC_PLUS_ACCT_STATUS_SUCCESS) { TACDEBUG((LOG_DEBUG, "%s: accounted ok", __FUNCTION__)); *ret_err_msg = NULL; } else { /* return pointer to server message */ syslog(LOG_ERR, "%s: accounting failed, server reply was %d " "(%s)", __FUNCTION__, r, server->server_msg); if (*ret_err_msg == NULL) { *ret_err_msg = "Accounting failed"; } err = -1; goto bail; } bail: if (tb) { free(tb); } return(err); }
/* * reads packet from TACACS+ server; returns: NULL if the authentication * succeded string pointer if it failed */ int tac_authen_read(tacacs_conf_t *conf, tacacs_server_t *server, const char **ret_err_msg, int *ret_status) { HDR th; struct authen_reply *tb = NULL; int len_from_header, r, len_from_body; int serv_msg_len, data_len; char *msg = NULL; int err = 0; if (ret_status) { *ret_status = TAC_PLUS_AUTHEN_STATUS_ERROR; } if (ret_err_msg == NULL) { syslog(LOG_ERR, "%s: error no err msg buffer provided", __FUNCTION__); err = -1; goto bail; } *ret_err_msg = NULL; if (server == NULL) { syslog(LOG_ERR, "%s: error no TACACS+ server specified", __FUNCTION__); err = -1; *ret_err_msg = no_server_err_msg; goto bail; } /* Free from any previous server messages */ reset_server(server, FALSE); /* * read the reply header * XXX it would appear that the reads are being done on non-blocking * sockets. This should be fixed to deal with partial reads. */ r = read(conf->sockfd, &th, TAC_PLUS_HDR_SIZE); if (r < TAC_PLUS_HDR_SIZE) { syslog(LOG_ERR, "%s: error reading authen header, read %d of %d: %m", __FUNCTION__, r, TAC_PLUS_HDR_SIZE); *ret_err_msg = system_err_msg; err = -1; goto bail; } /* check the reply fields in header */ err = _tac_check_header(&th, TAC_PLUS_AUTHEN, server, ret_err_msg); if (err) { goto bail; } len_from_header = ntohl(th.datalength); tb = (struct authen_reply *) xcalloc(1, len_from_header); /* read reply packet body */ r = read(conf->sockfd, tb, len_from_header); if (r < len_from_header) { syslog(LOG_ERR, "%s: incomplete message body, %d bytes, expected %d: %m", __FUNCTION__, r, len_from_header); *ret_err_msg = system_err_msg; err = -1; goto bail; } /* decrypt the body */ _tac_crypt(server->secret, (u_char *) tb, &th, len_from_header); serv_msg_len = ntohs(tb->msg_len); 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) + serv_msg_len + data_len; if (len_from_header != len_from_body) { syslog(LOG_ERR, "%s: invalid reply content, incorrect key? (calc %d, hdr %d)", __FUNCTION__, len_from_body, len_from_header); *ret_err_msg = system_err_msg; err = -1; goto bail; } /* Save off any message or data returned by the server */ if (serv_msg_len) { server->server_msg = (char *) xcalloc(1, serv_msg_len); bcopy(tb + TAC_AUTHEN_REPLY_FIXED_FIELDS_SIZE, server->server_msg, serv_msg_len); /* Show user what the server said */ *ret_err_msg = server->server_msg; } if (data_len) { server->data = (char *) xcalloc(1, data_len); bcopy(tb + TAC_AUTHEN_REPLY_FIXED_FIELDS_SIZE + serv_msg_len, server->data, data_len); } /* save status and clean up */ r = tb->status; if (ret_status) { *ret_status = r; } switch (r) { case TAC_PLUS_AUTHEN_STATUS_PASS: /* server authenticated username and password successfully */ break; case TAC_PLUS_AUTHEN_STATUS_FAIL: if (*ret_err_msg == NULL) { *ret_err_msg = auth_fail_msg; } err = -1; break; case TAC_PLUS_AUTHEN_STATUS_GETDATA: case TAC_PLUS_AUTHEN_STATUS_GETUSER: case TAC_PLUS_AUTHEN_STATUS_GETPASS: break; case TAC_PLUS_AUTHEN_STATUS_ERROR: syslog(LOG_ERR, "%s: TACACS+ authentication failed, server detected error", __FUNCTION__); err = -1; break; case TAC_PLUS_AUTHEN_STATUS_RESTART: case TAC_PLUS_AUTHEN_STATUS_FOLLOW: default: syslog(LOG_ERR, "%s: TACACS+ authentication failed, unexpected status %#x", __FUNCTION__, r); if (*ret_err_msg == NULL) { *ret_err_msg = unexpected_status_msg; } err = -1; break; } bail: if (err && (*ret_err_msg == NULL)) { /* At least say something bad */ *ret_err_msg = bad_login_msg; } if (tb) { free(tb); tb = NULL; } return(err); }