/* * Write accounting data to syslog */ int do_acct_syslog(struct acct_rec *rec) { char *acct_type; char *cmdbuf; int written, i; size_t bufsize; bufsize = 1024; cmdbuf = tac_malloc(bufsize); memset(cmdbuf, 0, bufsize); written = 0; switch(rec->acct_type) { case ACCT_TYPE_UPDATE: acct_type = "update"; break; case ACCT_TYPE_START: acct_type = "start"; break; case ACCT_TYPE_STOP: acct_type = "stop"; break; default: acct_type = "default"; } for (i = 0; i < rec->num_args; i++) { /* possible 4 spaces and and a null terminator == 5 */ if ((strlen(rec->args[i]) + written + 5) > bufsize) { cmdbuf = tac_realloc(cmdbuf, strlen(rec->args[i]) + written + 5); bufsize += strlen(rec->args[i]) + written + 5; } strncat(cmdbuf, rec->args[i], strlen(rec->args[i])); written += strlen(rec->args[i]); if (i < (rec->num_args - 1)) { strncat(cmdbuf, " ", 4); written += 4; } } syslog(LOG_INFO, "%s %s %s %s %s %s", ((rec->identity->NAS_name) && rec->identity->NAS_name[0]) ? rec->identity->NAS_name : "unknown", ((rec->identity->username) && rec->identity->username[0]) ? rec->identity->username : "******", ((rec->identity->NAS_port) && rec->identity->NAS_port[0]) ? rec->identity->NAS_port : "unknown", ((rec->identity->NAC_address) && rec->identity->NAC_address[0]) ? rec->identity->NAC_address : "unknown", acct_type, cmdbuf); free(cmdbuf); return 0; }
/* send an accounting response packet */ void send_acct_reply(u_char status, char *msg, char *data) { u_char *pak, *p; HDR *hdr; int len; struct acct_reply *reply; int msg_len, data_len; msg_len = msg ? strlen(msg) : 0; data_len = data ? strlen(data) : 0; len = TAC_PLUS_HDR_SIZE + TAC_ACCT_REPLY_FIXED_FIELDS_SIZE + msg_len + data_len; pak = (u_char *)tac_malloc(len); reply = (struct acct_reply *)(pak + TAC_PLUS_HDR_SIZE); hdr = (HDR *)pak; memset(pak, 0, len); hdr->version = TAC_PLUS_VER_0; hdr->type = TAC_PLUS_ACCT; hdr->seq_no = ++session.seq_no; hdr->flags = TAC_PLUS_UNENCRYPTED; if (!(session.flags & SESS_NO_SINGLECONN)) hdr->flags |= (session.peerflags & TAC_PLUS_SINGLE_CONNECT_FLAG); hdr->session_id = htonl(session.session_id); hdr->datalength = htonl(len - TAC_PLUS_HDR_SIZE); reply->status = status; reply->msg_len = msg_len; reply->data_len = data_len; p = pak + TAC_PLUS_HDR_SIZE + TAC_ACCT_REPLY_FIXED_FIELDS_SIZE; memcpy(p, msg, msg_len); p += msg_len; memcpy(p, data, data_len); if (debug & DEBUG_PACKET_FLAG) { report(LOG_DEBUG, "Writing %s size=%d", summarise_outgoing_packet_type(pak), len); dump_tacacs_pak(pak); } reply->msg_len = ntohs(reply->msg_len); reply->data_len = ntohs(reply->data_len); write_packet(pak); free(pak); return; }
static void declare(char *name, int value) { KEYWORD *n; KEYWORD *k = (KEYWORD *)tac_malloc(sizeof(KEYWORD)); k->word = tac_strdup(name); k->value = value; n = hash_add_entry(wordtable, (void *) k); if (n) { report(LOG_ERR, "Attempt to multiply define keyword %s", name); tac_exit(1); } }
char * tac_realloc(char *ptr, int size) { char *p; if (ptr == NULL) { /* realloc(0, size) is not portable */ p = tac_malloc(size); } else { p = (char *)realloc(ptr, size); } if (p == NULL) { report(LOG_ERR, "realloc %d failure", size); tac_exit(1); } return(p); }
/* * Reassemble the command arguments as typed by the user, out of the * array of args we received. Return "" if there are no arguments. */ static char * assemble_args(struct author_data *data) { char *buf; int i; char *nas_arg, *v; int len; len = 0; for (i = 0; i < data->num_in_args; i++) { nas_arg = data->input_args[i]; if (strncmp(nas_arg, "cmd-arg", strlen("cmd-arg")) == 0) { v = value(nas_arg); if (v != NULL) len += strlen(v) + 1; } } if (len <= 0) { return(tac_strdup("")); } buf = tac_malloc(len); buf[0] = '\0'; for (i = 0; i < data->num_in_args; i++) { nas_arg = data->input_args[i]; if (strncmp(nas_arg, "cmd-arg", strlen("cmd-arg"))) continue; v = value(nas_arg); if (!v) { free(buf); return(NULL); } strncat(buf, v, len - 1); len -= strlen(v); if (i < (data->num_in_args - 1)) { strncat(buf, " ", len - 1); len -= 1; } } return(buf); }
char * tac_make_string(u_char *p, int len) { char *string; int new_len = len; /* * Add space for a null terminator if needed. Also, no telling * what various mallocs will do when asked for a length of zero. */ if (len == 0 || p[len - 1]) new_len++; string = (char *)tac_malloc(new_len); memset(string, 0, new_len); memcpy(string, p, len); return(string); }
/* * Return 0 means data->status is valid. * protocol is only valid if svc == ppp. */ static int authorize_svc(char *user, int svc, char *protocol, char *svcname, struct author_data *data) { int max_args; char **out_args, **outp; char *nas_arg, *cfg_arg; int i, j; char **cfg_args; char **cfg_argp; int deny_by_default; NODE *node; int replaced = 0; int added = 0; int cfg_cnt; /* Does this service exist? */ node = cfg_get_svc_node(user, svc, protocol, svcname, TAC_PLUS_RECURSE); if (!node) { /* Service not found. If the default is permit, or this is an * PPP/LCP request and other ppp services are configured, * we'll allow it. */ if (cfg_user_svc_default_is_permit(user)) { if (debug & DEBUG_AUTHOR_FLAG) report(LOG_DEBUG, "svc=%s protocol=%s svcname=%s not found, " "permitted by default", cfg_nodestring(svc), protocol ? protocol : "", svcname ? svcname : ""); data->status = AUTHOR_STATUS_PASS_ADD; data->num_out_args = 0; data->output_args = NULL; return(0); } if (ppp_lcp_allowed(svc, protocol, user)) { data->status = AUTHOR_STATUS_PASS_ADD; data->num_out_args = 0; data->output_args = NULL; return(0); } if (debug & DEBUG_AUTHOR_FLAG) report(LOG_DEBUG, "svc=%s protocol=%s not found, denied by default", cfg_nodestring(svc), protocol ? protocol : ""); data->status = AUTHOR_STATUS_FAIL; data->num_out_args = 0; data->output_args = NULL; return(0); } /* Get server args configured in the config file. */ cfg_args = cfg_get_svc_attrs(node, &deny_by_default); /* Check the nas args for well-formedness */ for (i = 0; i < data->num_in_args; i++) { if (!arg_ok(data->input_args[i])) { char buf[MAX_INPUT_LINE_LEN+50]; snprintf(buf, sizeof(buf), "Illegal arg %s from NAS", data->input_args[i]); data->status = AUTHOR_STATUS_ERROR; data->admin_msg = tac_strdup(buf); report(LOG_ERR, "%s: Error %s", session.peer, buf); /* free any server arguments */ for (cfg_argp = cfg_args; cfg_args && *cfg_argp; cfg_argp++) free(*cfg_argp); free(cfg_args); return(0); } } /* How many configured AV pairs are there ? */ for (cfg_cnt = 0; cfg_args && cfg_args[cfg_cnt];) cfg_cnt++; /* Allocate space for in + out args */ max_args = cfg_cnt + data->num_in_args; out_args = (char **)tac_malloc((max_args + 1) * sizeof(char *)); outp = out_args; data->num_out_args = 0; memset(out_args, 0, (max_args + 1) * sizeof(char *)); for (i = 0; i < data->num_in_args; i++) { nas_arg = data->input_args[i]; /* always pass these pairs through unchanged */ if (match_attrs(nas_arg, "service=") || match_attrs(nas_arg, "protocol=") || match_attrs(nas_arg, "cmd=")) { if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "nas:%s (passed thru)", nas_arg); } *outp++ = tac_strdup(nas_arg); data->num_out_args++; continue; } /* NAS AV pair is mandatory */ if (mandatory(nas_arg)) { /* * a). look for an exact attribute,value match in the daemon's * mandatory list. If found, add the AV pair to the output */ for (j = 0; j < cfg_cnt; j++) { cfg_arg = cfg_args[j]; if (optional(cfg_arg)) continue; if (STREQ(nas_arg, cfg_arg)) { if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "nas:%s, svr:%s -> add %s (a)", nas_arg, cfg_arg, nas_arg); } *outp++ = tac_strdup(nas_arg); data->num_out_args++; goto next_nas_arg; } } /* * b). If an exact match doesn't exist, look in the daemon's * optional list for the first attribute match. If found, add the * NAS AV pair to the output */ for (j = 0; j < cfg_cnt; j++) { cfg_arg = cfg_args[j]; if (mandatory(cfg_arg)) continue; if (match_attrs(nas_arg, cfg_arg)) { if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "nas:%s, svr:%s -> add %s (b)", nas_arg, cfg_arg, nas_arg); } *outp++ = tac_strdup(nas_arg); data->num_out_args++; goto next_nas_arg; } } /* * c). If no attribute match exists, deny the command if the * default is to deny */ if (deny_by_default) { data->status = AUTHOR_STATUS_FAIL; if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "nas:%s svr:absent, default=deny -> " "denied (c)", nas_arg); } if (out_args) { for (i = 0; i < data->num_out_args; i++) free(out_args[i]); free(out_args); } data->num_out_args = 0; data->output_args = NULL; /* free the server arguments */ for (cfg_argp = cfg_args; *cfg_argp; cfg_argp++) free(*cfg_argp); free(cfg_args); return(0); } /* * d). If the default is permit, add the NAS AV pair to the output */ if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "nas:%s, svr:absent, default=permit -> add %s (d)", nas_arg, nas_arg); } *outp++ = tac_strdup(nas_arg); data->num_out_args++; goto next_nas_arg; } else { /* * NAS AV pair is Optional * * e). look for an exact attribute,value match in the mandatory * list. If found, add DAEMON's AV pair to output */ for (j = 0; j < cfg_cnt; j++) { cfg_arg = cfg_args[j]; if (optional(cfg_arg)) continue; if (match_attrs(nas_arg, cfg_arg) && match_values(nas_arg, cfg_arg)) { if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "nas:%s svr:%s -> replace with %s " "(e)", nas_arg, cfg_arg, cfg_arg); } *outp++ = tac_strdup(cfg_arg); data->num_out_args++; replaced++; goto next_nas_arg; } } /* * f). If not found, look for the first attribute match in the * mandatory list. If found, add DAEMONS's AV pair to output */ for (j = 0; j < cfg_cnt; j++) { cfg_arg = cfg_args[j]; if (optional(cfg_arg)) continue; if (match_attrs(nas_arg, cfg_arg)) { if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "nas:%s svr:%s -> replace with %s " "(f)", nas_arg, cfg_arg, cfg_arg); } *outp++ = tac_strdup(cfg_arg); data->num_out_args++; replaced++; goto next_nas_arg; } } /* * g). If no mandatory match exists, look for an exact * attribute,value pair match among the daemon's optional AV * pairs. If found add the DAEMON's matching AV pair to the * output. */ for (j = 0; j < cfg_cnt; j++) { cfg_arg = cfg_args[j]; if (!optional(cfg_arg)) continue; if (match_attrs(nas_arg, cfg_arg) && match_values(nas_arg, cfg_arg)) { if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "nas:%s svr:%s -> replace with %s " "(g)", nas_arg, cfg_arg, cfg_arg); } *outp++ = tac_strdup(cfg_arg); data->num_out_args++; replaced++; goto next_nas_arg; } } /* * h). If no exact match exists, locate the first attribute match * among the daemon's optional AV pairs. If found add the DAEMON's * matching AV pair to the output */ for (j = 0; j < cfg_cnt; j++) { cfg_arg = cfg_args[j]; if (!optional(cfg_arg)) continue; if (match_attrs(nas_arg, cfg_arg)) { if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "nas:%s svr:%s -> replace with %s " "(h)", nas_arg, cfg_arg, cfg_arg); } *outp++ = tac_strdup(cfg_arg); data->num_out_args++; replaced++; goto next_nas_arg; } } /* * i). If no match is found, delete the AV pair if default is deny */ if (deny_by_default) { if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "nas:%s svr:absent/deny -> delete %s (i)", nas_arg, nas_arg); } replaced++; goto next_nas_arg; } /* j). If the default is permit add the NAS AV pair to the output */ if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "nas:%s svr:absent/permit -> add %s (j)", nas_arg, nas_arg); } *outp++ = tac_strdup(nas_arg); data->num_out_args++; goto next_nas_arg; } next_nas_arg:; } /* * k). After all AV pairs have been processed, for each mandatory DAEMON * AV pair, if there is no attribute match already in the output list, add * the AV pair (add only one AV pair for each mandatory attribute) */ for (i = 0; i < cfg_cnt; i++) { cfg_arg = cfg_args[i]; if (!mandatory(cfg_arg)) continue; for (j = 0; j < data->num_out_args; j++) { char *output_arg = out_args[j]; if (match_attrs(cfg_arg, output_arg)) { goto next_cfg_arg; } } /* Attr is required by daemon but not present in output. Add it */ if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "nas:absent, server:%s -> add %s (k)", cfg_arg, cfg_arg); } added++; *outp++ = tac_strdup(cfg_arg); data->num_out_args++; next_cfg_arg: ; } /* * If we replaced or deleted some pairs we must return the entire list we * have constructed */ if (replaced) { if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "replaced %d args", replaced); } data->status = AUTHOR_STATUS_PASS_REPL; data->output_args = out_args; /* free the server arguments */ for (cfg_argp = cfg_args; *cfg_argp; cfg_argp++) free(*cfg_argp); free(cfg_args); return(0); } /* * We added something not on the original nas list, but did not replace or * delete anything. We should return only the additions */ if (added) { if (debug & DEBUG_AUTHOR_FLAG) report(LOG_DEBUG, "added %d args", added); /* throw away output args which are just copies of the input args */ for (i = 0; i < data->num_in_args; i++) { if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "out_args[%d] = %s input copy discarded", i, out_args[i]); } free(out_args[i]); out_args[i] = NULL; } /* * Now compact the new args added to the end of the array down to the * beginning */ j = 0; for (i = data->num_in_args; i < data->num_out_args; i++) { if (out_args[j]) /* we goofed */ report(LOG_ERR, "%s: out_args[%d] should be NULL", session.peer, j); if (!out_args[i]) /* we goofed */ report(LOG_ERR, "%s: out_args[%d] should not be NULL", session.peer, i); if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "out_args[%d] = %s compacted to out_args[%d]", i, out_args[i], j); } out_args[j++] = out_args[i]; out_args[i] = NULL; } data->num_out_args = j; if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "%d output args", data->num_out_args); } /* should/could do a realloc here but it won't matter */ data->status = AUTHOR_STATUS_PASS_ADD; data->output_args = out_args; /* free the server arguments */ for (cfg_argp = cfg_args; *cfg_argp; cfg_argp++) free(*cfg_argp); free(cfg_args); return(0); } /* * no additions or replacements. Input and output are identical. Don't * need to return anything */ if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "added %d", added); } data->status = AUTHOR_STATUS_PASS_ADD; if (out_args) { for (i = 0; i < data->num_out_args; i++) { free(out_args[i]); } free(out_args); } /* Final sanity check */ if (data->num_out_args != data->num_in_args) { data->status = AUTHOR_STATUS_ERROR; data->admin_msg = tac_strdup("Bad output arg cnt from do_author"); report(LOG_ERR, "%s: Error %s", session.peer, data->admin_msg); /* free the server arguments */ for (cfg_argp = cfg_args; *cfg_argp; cfg_argp++) free(*cfg_argp); free(cfg_args); return(0); } data->num_out_args = 0; data->output_args = NULL; /* free the server arguments */ for (cfg_argp = cfg_args; *cfg_argp; cfg_argp++) free(*cfg_argp); free(cfg_args); return(0); }
static void account(u_char *pak) { struct acct *acct_pak; u_char *p, *argsizep; struct acct_rec rec; struct identity identity; char **cmd_argp; int i, errors = 0, status; acct_pak = (struct acct *) (pak + TAC_PLUS_HDR_SIZE); /* Fill out accounting record structure */ memset(&rec, 0, sizeof(struct acct_rec)); if (acct_pak->flags & TAC_PLUS_ACCT_FLAG_WATCHDOG) rec.acct_type = ACCT_TYPE_UPDATE; if (acct_pak->flags & TAC_PLUS_ACCT_FLAG_START) rec.acct_type = ACCT_TYPE_START; if (acct_pak->flags & TAC_PLUS_ACCT_FLAG_STOP) rec.acct_type = ACCT_TYPE_STOP; rec.authen_method = acct_pak->authen_method; rec.authen_type = acct_pak->authen_type; rec.authen_service = acct_pak->authen_service; /* start of variable length data is here */ p = pak + TAC_PLUS_HDR_SIZE + TAC_ACCT_REQ_FIXED_FIELDS_SIZE; /* skip arg cnts */ p += acct_pak->arg_cnt; /* zero out identity struct */ memset(&identity, 0, sizeof(struct identity)); identity.username = tac_make_string(p, (int)acct_pak->user_len); p += acct_pak->user_len; identity.NAS_name = tac_strdup(session.peer); identity.NAS_port = tac_make_string(p, (int)acct_pak->port_len); p += acct_pak->port_len; if (acct_pak->port_len <= 0) { strcpy(session.port, "unknown-port"); } else { strcpy(session.port, identity.NAS_port); } identity.NAC_address = tac_make_string(p, (int)acct_pak->rem_addr_len); p += acct_pak->rem_addr_len; identity.priv_lvl = acct_pak->priv_lvl; rec.identity = &identity; /* Now process cmd args */ argsizep = pak + TAC_PLUS_HDR_SIZE + TAC_ACCT_REQ_FIXED_FIELDS_SIZE; cmd_argp = (char **)tac_malloc(acct_pak->arg_cnt * sizeof(char *)); for (i = 0; i < (int)acct_pak->arg_cnt; i++) { cmd_argp[i] = tac_make_string(p, *argsizep); p += *argsizep++; } rec.args = cmd_argp; rec.num_args = acct_pak->arg_cnt; #ifdef MAXSESS /* Tally for MAXSESS counting */ loguser(&rec); #endif /* Do accounting */ if (wtmpfile) errors = do_wtmp(&rec); if (session.acctfile != NULL) errors += do_acct_file(&rec); if (session.flags & SESS_FLAG_ACCTSYSL) errors += do_acct_syslog(&rec); if (errors) { status = TAC_PLUS_ACCT_STATUS_ERROR; } else { status = TAC_PLUS_ACCT_STATUS_SUCCESS; } send_acct_reply(status, rec.msg, rec.admin_msg); free(identity.username); free(identity.NAS_name); free(identity.NAS_port); free(identity.NAC_address); for (i = 0; i < (int)acct_pak->arg_cnt; i++) { free(cmd_argp[i]); } free(cmd_argp); if (rec.msg) free(rec.msg); if (rec.admin_msg) free(rec.admin_msg); }
/* * read a packet from the wire, and decrypt it. Increment the global * seq_no return NULL on failure */ u_char * read_packet(void) { HDR hdr; u_char *pkt, *data; int len; char *tkey; if (debug & DEBUG_PACKET_FLAG) report(LOG_DEBUG, "Waiting for packet"); /* read a packet header */ len = sockread(session.sock, (u_char *)&hdr, TAC_PLUS_HDR_SIZE, cfg_get_readtimeout()); if (len != TAC_PLUS_HDR_SIZE) { report(LOG_DEBUG, "Read %d bytes from %s %s, expecting %d", len, session.peer, session.port, TAC_PLUS_HDR_SIZE); return(NULL); } session.peerflags = hdr.flags; if ((hdr.version & TAC_PLUS_MAJOR_VER_MASK) != TAC_PLUS_MAJOR_VER) { report(LOG_ERR, "%s: Illegal major version specified: found %d wanted " "%d\n", session.peer, hdr.version, TAC_PLUS_MAJOR_VER); return(NULL); } /* get memory for the packet */ len = TAC_PLUS_HDR_SIZE + ntohl(hdr.datalength); if ((ntohl(hdr.datalength) & ~0xffffUL) || (len < TAC_PLUS_HDR_SIZE) || (len > 0x10000)) { report(LOG_ERR, "%s: Illegal data size: %lu\n", session.peer, ntohl(hdr.datalength)); return(NULL); } pkt = (u_char *)tac_malloc(len); /* initialise the packet */ memcpy(pkt, &hdr, TAC_PLUS_HDR_SIZE); /* the data start here */ data = pkt + TAC_PLUS_HDR_SIZE; /* read the rest of the packet data */ if (sockread(session.sock, data, ntohl(hdr.datalength), cfg_get_readtimeout()) != ntohl(hdr.datalength)) { report(LOG_ERR, "%s: start_session: bad socket read", session.peer); free(pkt); return(NULL); } session.seq_no++; /* should now equal that of incoming packet */ session.last_exch = time(NULL); if (session.seq_no != hdr.seq_no) { report(LOG_ERR, "%s: Illegal session seq # %d != packet seq # %d", session.peer, session.seq_no, hdr.seq_no); free(pkt); return(NULL); } /* decrypt the data portion */ tkey = cfg_get_host_key(session.peerip); if (tkey == NULL && !STREQ(session.peer, session.peerip)) { tkey = cfg_get_host_prompt(session.peer); } if (tkey == NULL) tkey = session.key; if (md5_xor((HDR *)pkt, data, tkey)) { report(LOG_ERR, "%s: start_session error decrypting data", session.peer); free(pkt); return(NULL); } if (debug & DEBUG_PACKET_FLAG) report(LOG_DEBUG, "Read %s size=%d", summarise_incoming_packet_type(pkt), len); session.version = hdr.version; return(pkt); }
/* send an authorization reply packet */ void send_author_reply(u_char status, char *msg, char *data, int arg_cnt, char **args) { u_char *pak, *p; HDR *hdr; struct author_reply *reply; int msg_len; int len; int data_len; int i; data_len = (data ? strlen(data) : 0); msg_len = (msg ? strlen(msg) : 0); /* start calculating final packet size */ len = TAC_PLUS_HDR_SIZE + TAC_AUTHOR_REPLY_FIXED_FIELDS_SIZE + msg_len + data_len; for (i = 0; i < arg_cnt; i++) { /* space for the arg and its length */ len += strlen(args[i]) + 1; } pak = (u_char *)tac_malloc(len); memset(pak, 0, len); hdr = (HDR *)pak; reply = (struct author_reply *) (pak + TAC_PLUS_HDR_SIZE); hdr->version = TAC_PLUS_VER_0; hdr->type = TAC_PLUS_AUTHOR; hdr->seq_no = ++session.seq_no; hdr->flags = TAC_PLUS_UNENCRYPTED; if (!(session.flags & SESS_NO_SINGLECONN)) hdr->flags |= (session.peerflags & TAC_PLUS_SINGLE_CONNECT_FLAG); hdr->session_id = htonl(session.session_id); hdr->datalength = htonl(len - TAC_PLUS_HDR_SIZE); reply->status = status; reply->msg_len = msg_len; reply->data_len = data_len; reply->arg_cnt = arg_cnt; p = pak + TAC_PLUS_HDR_SIZE + TAC_AUTHOR_REPLY_FIXED_FIELDS_SIZE; /* place arg sizes into packet */ for (i = 0; i < arg_cnt; i++) { *p++ = strlen(args[i]); } memcpy(p, msg, msg_len); p += msg_len; memcpy(p, data, data_len); p += data_len; /* copy arg bodies into packet */ for (i = 0; i < arg_cnt; i++) { int arglen = strlen(args[i]); memcpy(p, args[i], arglen); p += arglen; } if (debug & DEBUG_PACKET_FLAG) { report(LOG_DEBUG, "Writing %s size=%d", summarise_outgoing_packet_type(pak), len); dump_tacacs_pak(pak); } reply->msg_len = htons(reply->msg_len); reply->data_len = htons(reply->data_len); write_packet(pak); free(pak); return; }
/* * Come here when we receive an authorization START packet */ void author(u_char *pak) { HDR *hdr; struct author *apak; struct identity identity; #ifdef ACLS struct authen_data authen_data; #endif struct author_data author_data; u_char *p; u_char *argsizep; char **cmd_argp; int i, len; if (debug & DEBUG_AUTHOR_FLAG) report(LOG_DEBUG, "Start authorization request"); hdr = (HDR *)pak; apak = (struct author *)(pak + TAC_PLUS_HDR_SIZE); /* Do some sanity checks */ if (hdr->seq_no != 1) { send_error_reply(TAC_PLUS_AUTHOR, NULL); return; } /* Check if there's at least sizeof(struct author) of useful data */ if (ntohl(hdr->datalength) < TAC_AUTHOR_REQ_FIXED_FIELDS_SIZE) { report(LOG_ERR, "%s: author minimum payload length: %zu, got: %u", session.peer, TAC_AUTHOR_REQ_FIXED_FIELDS_SIZE, ntohl(hdr->datalength)); send_error_reply(TAC_PLUS_AUTHOR, NULL); return; } /* arg counts start here */ p = pak + TAC_PLUS_HDR_SIZE + TAC_AUTHOR_REQ_FIXED_FIELDS_SIZE; /* Length checks */ len = TAC_AUTHOR_REQ_FIXED_FIELDS_SIZE; len += apak->user_len + apak->port_len + apak->rem_addr_len + apak->arg_cnt; /* Is there enough space for apak->arg_cnt arguments? */ if (ntohl(hdr->datalength) < (TAC_AUTHOR_REQ_FIXED_FIELDS_SIZE + apak->arg_cnt)) { report(LOG_ERR, "%s: author minimum payload length: %zu, got: %u", session.peer, TAC_AUTHOR_REQ_FIXED_FIELDS_SIZE + apak->arg_cnt, ntohl(hdr->datalength)); send_error_reply(TAC_PLUS_AUTHOR, NULL); return; } for (i = 0; i < (int)apak->arg_cnt; i++) { len += p[i]; } if (len != ntohl(hdr->datalength)) { send_error_reply(TAC_PLUS_AUTHOR, NULL); return; } /* start of variable length data is here */ p = pak + TAC_PLUS_HDR_SIZE + TAC_AUTHOR_REQ_FIXED_FIELDS_SIZE; /* arg length data starts here */ argsizep = p; p += apak->arg_cnt; memset(&author_data, 0, sizeof(struct author_data)); /* The identity structure */ /* zero out identity struct */ memset(&identity, 0, sizeof(struct identity)); identity.username = tac_make_string(p, (int)apak->user_len); p += apak->user_len; identity.NAS_name = tac_strdup(session.peer); #ifdef ACLS identity.NAS_ip = tac_strdup(session.peerip); #endif identity.NAS_port = tac_make_string(p, (int)apak->port_len); p += apak->port_len; if (apak->port_len <= 0) { strcpy(session.port, "unknown-port"); } else { strcpy(session.port, identity.NAS_port); } identity.NAC_address = tac_make_string(p, (int)apak->rem_addr_len); p += apak->rem_addr_len; identity.priv_lvl = apak->priv_lvl; /* The author_data structure */ author_data.id = &identity; /* user id */ /* FIXME: validate these fields */ author_data.authen_method = apak->authen_method; author_data.authen_type = apak->authen_type; author_data.service = apak->service; author_data.num_in_args = apak->arg_cnt; /* Space for args + NULL */ cmd_argp = (char **)tac_malloc(apak->arg_cnt * sizeof(char *)); /* p points to the start of args. Step thru them making strings */ for (i = 0; i < (int)apak->arg_cnt; i++) { cmd_argp[i] = tac_make_string(p, *argsizep); p += *argsizep++; } author_data.input_args = cmd_argp; /* input command arguments */ #ifdef ACLS authen_data.NAS_id = &identity; if (verify_host(author_data.id->username, &authen_data, S_acl, TAC_PLUS_RECURSE) != S_permit) { author_data.status = AUTHOR_STATUS_FAIL; } else #endif if (do_author(&author_data)) { report(LOG_ERR, "%s: do_author returned an error", session.peer); send_author_reply(AUTHOR_STATUS_ERROR, author_data.msg, author_data.admin_msg, author_data.num_out_args, author_data.output_args); return; } /* Send a reply packet */ send_author_reply(author_data.status, author_data.msg, author_data.admin_msg, author_data.num_out_args, author_data.output_args); if (debug) report(LOG_INFO, "authorization query for '%s' %s from %s %s", author_data.id->username && author_data.id->username[0] ? author_data.id->username : "******", author_data.id->NAS_port && author_data.id->NAS_port[0] ? author_data.id->NAS_port : "unknown", session.peer, (author_data.status == AUTHOR_STATUS_PASS_ADD || author_data.status == AUTHOR_STATUS_PASS_REPL) ? "accepted" : "rejected"); /* free the input args */ if (author_data.input_args) { for (i = 0; i < author_data.num_in_args; i++) free(author_data.input_args[i]); free(author_data.input_args); author_data.input_args = NULL; } /* free the output args */ if (author_data.output_args) { for (i = 0; i < author_data.num_out_args; i++) free(author_data.output_args[i]); free(author_data.output_args); author_data.output_args = NULL; } if (author_data.msg) free(author_data.msg); if (author_data.admin_msg) free(author_data.admin_msg); free(identity.username); free(identity.NAS_name); #ifdef ACLS free(identity.NAS_ip); #endif free(identity.NAS_port); free(identity.NAC_address); }