/* 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) { 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"; char *token = NULL; u_char *pkt = NULL; 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; TACDEBUG(LOG_DEBUG, "%s: user '%s', tty '%s', rem_addr '%s', encrypt: %s", __FUNCTION__, user, tty, r_addr, (tac_encryption) ? "yes" : "no"); /* get size of submitted data */ user_len = strlen(user); chal_len = strlen(chal); pass_len = strlen(pass); port_len = strlen(tty); r_addr_len = strlen(r_addr); if (!strcmp(tac_login, "chap")) { u_char digest[MD5_LBLOCK]; digest_chap(digest, id, pass, pass_len, chal, chal_len); token_len = sizeof(id) + chal_len + sizeof(digest); token = xcalloc(1, token_len); token[0] = id; memcpy(token + sizeof(id), chal, chal_len); memcpy(token + sizeof(id) + chal_len, digest, sizeof(digest)); } else { token = xstrdup(pass); 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", __FUNCTION__, w, TAC_PLUS_HDR_SIZE); free(token); free(pkt); free(th); return LIBTAC_STATUS_WRITE_ERR; } /* build the packet */ pkt = (u_char *) 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", __FUNCTION__, bodylength, pkt_len); free(token); free(pkt); free(th); return LIBTAC_STATUS_ASSEMBLY_ERR; } /* encrypt the body */ _tac_crypt(pkt, th); 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", __FUNCTION__, w, pkt_len); 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 */
/* Send authorization request to the server, along with attributes specified in attribute list prepared with tac_add_attrib. */ int tac_author_send(int fd, const char *user, const char *tty, struct tac_attrib *attr) { HDR *th; struct author tb; u_char user_len, port_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; /* packet building pointer */ /* u_char *pktp; */ /* obsolete */ int ret = 0; th=_tac_req_header(TAC_PLUS_AUTHOR); /* set header options */ th->version=TAC_PLUS_VER_0; th->encryption=tac_encryption ? TAC_PLUS_ENCRYPTED : TAC_PLUS_CLEAR; DEBUG_TACPLUS("user '%s', tty '%s', encrypt: %s", user, tty, tac_encryption ? "yes" : "no"); user_len=(u_char) strlen(user); port_len=(u_char) strlen(tty); tb.authen_method=AUTHEN_METH_TACACSPLUS; tb.priv_lvl=TAC_PLUS_PRIV_LVL_MIN; 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_PLUS_AUTHEN_SVC_PPP; tb.user_len=user_len; tb.port_len=port_len; tb.rem_addr_len=0; /* 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 = 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); /* fill user and port fields */ PUTATTR(user, user_len) PUTATTR(tty, port_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) { DEBUG_TACPLUS("author hdr send failed: wrote %d of %d", w, TAC_PLUS_HDR_SIZE); ret = -1; } /* encrypt packet body */ _tac_crypt(pkt, th, pkt_len); /* write body */ w=write(fd, pkt, pkt_len); if(w < pkt_len) { DEBUG_TACPLUS("author body send failed: wrote %d of %d", w, pkt_len); ret = -1; } free(pkt); free(th); return(ret); }
/* * 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", \ __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; } /* 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 = 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); /* 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",\ __FUNCTION__, w, TAC_PLUS_HDR_SIZE); free(pkt); free(th); return LIBTAC_STATUS_WRITE_ERR; } /* encrypt packet body */ _tac_crypt(pkt, th); /* 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); ret = LIBTAC_STATUS_WRITE_ERR; } free(pkt); free(th); TACDEBUG(LOG_DEBUG, "%s: exit status=%d", __FUNCTION__, ret); return ret; }
/* 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(int fd, char *pass) { 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 = 3; /* 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",\ __FUNCTION__, 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",\ __FUNCTION__, 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",\ __FUNCTION__, w, pkt_len)) ret=LIBTAC_STATUS_WRITE_ERR; } free(pkt); free(th); TACDEBUG((LOG_DEBUG, "%s: exit status=%d", __FUNCTION__, ret)) return ret; } /* tac_cont_send */
/* 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)) }
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); }
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); }
/* 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, int action, int ctrl) { 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 ((tac_login != NULL) && (strcmp(tac_login,"login") == 0)) { 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; if (ctrl & PAM_TAC_DEBUG) TACDEBUG((LOG_DEBUG, "%s: user '%s', tty '%s', rem_addr '%s', encrypt: %s", \ __FUNCTION__, user, tty, r_addr, \ (tac_encryption) ? "yes" : "no")) if ((tac_login != NULL) && (strcmp(tac_login,"chap") == 0)) { chal_len = strlen(chal); mdp_len = sizeof(u_char) + strlen(pass) + chal_len; mdp = (u_char *) xcalloc(1, mdp_len); mdp[0] = 5; memcpy(&mdp[1], pass, strlen(pass)); memcpy(mdp + strlen(pass) + 1, chal, chal_len); MD5Init(&mdcontext); MD5Update(&mdcontext, mdp, mdp_len); MD5Final((u_char *) digest, &mdcontext); free(mdp); token = (char*) 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 = 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 == NULL) { /* 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; /* 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",\ __FUNCTION__, w, TAC_PLUS_HDR_SIZE)) free(token); free(pkt); free(th); return LIBTAC_STATUS_WRITE_ERR; } /* build the packet */ pkt = (u_char *) 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",\ __FUNCTION__, 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",\ __FUNCTION__, w, pkt_len)) ret = LIBTAC_STATUS_WRITE_ERR; } /* Packet Debug (In 'debug tacacs packet' format */ if (ctrl & PAM_TAC_PACKET_DEBUG) { char *action_str; char *type_str; char *service_str; authen_action_string(&action_str, tb.action); authen_type_string(&type_str, tb.authen_type); authen_service_string(&service_str, tb.service); TACDEBUG((LOG_DEBUG, "T+: Version %u (0x%02X), type %u, seq %u, encryption %u", th->version, th->version, th->type, th->seq_no, th->encryption)) TACDEBUG((LOG_DEBUG, "T+: session_id %u (0x%08X), dlen %u (0x%02X)", th->session_id, th->session_id, th->datalength, th->datalength)) TACDEBUG((LOG_DEBUG, "T+: type:AUTHEN/START, priv_lvl:%u action:%s %s", tb.priv_lvl, action_str, type_str)) TACDEBUG((LOG_DEBUG, "T+: svc:%s user_len:%u port_len:%u (0x%02X) raddr_len:%u (0x%02X) data_len:%d", service_str, tb.user_len, tb.port_len, tb.port_len, tb.r_addr_len, tb.r_addr_len, tb.data_len)) TACDEBUG((LOG_DEBUG, "T+: user: %s", user)) TACDEBUG((LOG_DEBUG, "T+: port: %s", tty)) TACDEBUG((LOG_DEBUG, "T+: rem_addr: %s", r_addr)) /*TACDEBUG((LOG_DEBUG, "T+: data: %s", token)) hide user password (!)*/ TACDEBUG((LOG_DEBUG, "T+: data: <hidden>")) TACDEBUG((LOG_DEBUG, "T+: End Packet")) free(action_str); free(type_str); free(service_str); } free(token); free(pkt); free(th); if (ctrl & PAM_TAC_DEBUG) TACDEBUG((LOG_DEBUG, "%s: exit status=%d", __FUNCTION__, ret)) return ret; } /* tac_authen_send */
/* * 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); }