static int init(void) { if (initialised) cfg_clean_config(); report(LOG_NOTICE, "Reading config"); if (!session.cfgfile) { report(LOG_ERR, "no config file specified"); tac_exit(1); } /* read the config file */ if (cfg_read_config(session.cfgfile)) { report(LOG_ERR, "Parsing %s", session.cfgfile); tac_exit(1); } if (session.acctfile == NULL && !(session.flags & SESS_FLAG_ACCTSYSL)) session.acctfile = tac_strdup(TACPLUS_ACCTFILE); initialised++; reinitialize = 0; report(LOG_NOTICE, "Version %s Initialized %d", version, initialised); return 0; }
/* * Interpolate values of dollar variables into a string. Determine values * for the various $ variables by looking in the authorization data. */ static char * substitute(char *string, struct author_data *data) { char *cp; char out[MAX_INPUT_LINE_LEN], *outp; char sym[MAX_INPUT_LINE_LEN], *symp; char *value, *valuep; if (debug & DEBUG_AUTHOR_FLAG) report(LOG_DEBUG, "substitute: %s", string); cp = string; outp = out; while (*cp) { if (*cp != DOLLARSIGN) { *outp++ = *cp++; continue; } cp++; /* skip dollar sign */ symp = sym; /* does it have curly braces e.g. ${foo} ? */ if (*cp == '{') { cp++; /* skip { */ while (*cp && *cp != '}') *symp++ = *cp++; cp++; /* skip } */ } else { /* copy symbol into sym */ while (*cp && isalpha((int) *cp)) *symp++ = *cp++; } *symp = '\0'; /* lookup value */ if (debug & DEBUG_SUBST_FLAG) report(LOG_DEBUG, "Lookup %s", sym); valuep = value = lookup(sym, data); if (debug & DEBUG_SUBST_FLAG) report(LOG_DEBUG, "Expands to: %s", value); /* copy value into output */ while (valuep && *valuep) *outp++ = *valuep++; free(value); } *outp++ = '\0'; if (debug & DEBUG_AUTHOR_FLAG) report(LOG_DEBUG, "Dollar substitution: %s", out); return(tac_strdup(out)); }
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); } }
/* * 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); }
/* * 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); }
/* Return 0 is data->status is valid */ int do_author(struct author_data *data) { char *username = data->id->username; int status; int svc; char *cmd, *protocol, *svcname; status = 0; protocol = NULL; data->status = AUTHOR_STATUS_FAIL; /* for safety */ data->output_args = NULL; data->num_out_args = 0; if (debug & DEBUG_AUTHOR_FLAG) report(LOG_DEBUG, "do_author: user='******'", username); if (!cfg_user_exists(username) && cfg_user_exists(DEFAULT_USERNAME)) { if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "Authorizing user '%s' instead of '%s'", DEFAULT_USERNAME, username); } username = DEFAULT_USERNAME; } /* See if there's a program defined which will do authorization for us */ if (pre_authorization(username, data)) return(0); /* * Decide what kind of authorization request this is. Currently * one of: exec, cmd, slip, arap, ppp or <string> * * If it's a command typed to the exec, return its text in cmd. * * If it's a ppp request, return the protocol name in protocol. */ svc = get_nas_svc(data, &cmd, &protocol, &svcname); if (!svc) { /* if we can't identify the service in the request it's an error */ data->status = AUTHOR_STATUS_ERROR; data->admin_msg = tac_strdup("No identifiable service/protocol in authorization request"); if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "user %s %s", username, data->admin_msg); } return(0); } if (debug & DEBUG_AUTHOR_FLAG) report(LOG_DEBUG, "user '%s' found", username); #ifdef MAXSESS /* Never permit if they're going over their session limit */ switch (svc) { case N_svc_arap: case N_svc_ppp: case N_svc_slip: case N_svc_exec: /* case N_svc: */ if (maxsess_check_count(username, data)) { return(0); } default: break; } #endif /* MAXSESS */ switch(svc) { /* XXX default: report(LOG_ERR, "%s: Bad service type %d", session.peer, svc); data->status = AUTHOR_STATUS_FAIL; return(0);*/ case N_svc_cmd: /* A command authorisation request */ status = authorize_cmd(username, cmd, data); break; case N_svc_exec: if (authorize_exec(username, data)) return(0); /* FALLTHRU */ case N_svc_arap: case N_svc_ppp: case N_svc_slip: status = authorize_svc(username, svc, protocol, NULL, data); break; case N_svc: status = authorize_svc(username, svc, protocol, svcname, data); break; } post_authorization(username, data); return(status); }
/* * Is an exec command authorized per our database(s)? Return 0 if status is * valid. */ static int authorize_cmd(char *user, char *cmd, struct author_data *data) { char buf[256]; NODE *node; char *args; int match; args = assemble_args(data); if (!cmd) { data->status = AUTHOR_STATUS_ERROR; data->admin_msg = tac_strdup("No command found"); report(LOG_ERR, "%s: %s %s", session.peer, cmd, data->admin_msg); data->num_out_args = 0; return(0); } if (debug & DEBUG_AUTHOR_FLAG) report(LOG_DEBUG, "authorize_cmd: user=%s, cmd=%s", user, cmd); node = cfg_get_cmd_node(user, cmd, TAC_PLUS_RECURSE); /* The command does not exist. Do the default */ if (!node) { if (cfg_user_svc_default_is_permit(user)) { if (debug & DEBUG_AUTHOR_FLAG) report(LOG_DEBUG, "cmd %s does not exist, permitted by default", cmd); data->status = AUTHOR_STATUS_PASS_ADD; data->num_out_args = 0; if (args) free(args); return(0); } if (debug & DEBUG_AUTHOR_FLAG) report(LOG_DEBUG, "cmd %s does not exist, denied by default", cmd); data->status = AUTHOR_STATUS_FAIL; data->num_out_args = 0; if (args) free(args); return(0); } /* The command exists. The default if nothing matches is DENY */ data->status = AUTHOR_STATUS_FAIL; data->num_out_args = 0; for (node = node->value1; node && args; node = node->next) { match = regexec((regex_t *)node->value1, args, 0, NULL, 0); if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_INFO, "line %d compare %s %s '%s' & '%s' %s", node->line, cmd, node->type == N_permit ? "permit" : "deny", node->value, args, (match == REG_NOMATCH ? "no match" : !match ? "match" : "regex failure")); } if (match == REG_NOMATCH) continue; if (match != REG_OK) { regerror(match, (regex_t *)node->value1, buf, 256); report(LOG_INFO, "regexec error: %s on line %d: %s", (char *)node->value, node->line, buf); continue; } switch (node->type) { case N_permit: if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "%s %s permitted by line %d", cmd, args, node->line); } data->status = AUTHOR_STATUS_PASS_ADD; data->num_out_args = 0; break; case N_deny: if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "%s %s denied by line %d", cmd, args, node->line); } data->status = AUTHOR_STATUS_FAIL; data->num_out_args = 0; break; default: data->status = AUTHOR_STATUS_ERROR; data->admin_msg = tac_strdup("Server error illegal configuration " "node"); report(LOG_ERR, "%s: %s %s %s", session.peer, cmd, args, data->admin_msg); break; } if (args) free(args); args = NULL; return(0); } if (args) free(args); return(0); }
/* * If an before-authorization program has been specified, call it. * * A return value of 1 means no further authorization is required */ static int pre_authorization(char *username, struct author_data *data) { int status; char **out_args; int out_cnt, i; char *cmd; char error_str[255]; int error_len = 255; out_cnt = 0; out_args = NULL; /* * If a before-authorization program exists, call it to see how to * proceed */ cmd = cfg_get_pvalue(username, TAC_IS_USER, S_before, TAC_PLUS_RECURSE); if (!cmd) return(0); if (debug & DEBUG_AUTHOR_FLAG) report(LOG_DEBUG, "Before authorization call: %s", cmd); status = call_pre_process(cmd, data, &out_args, &out_cnt, error_str, error_len); switch (status) { default: if (debug & DEBUG_AUTHOR_FLAG) report(LOG_DEBUG, "cmd %s returns %d (unrecognised value)", cmd, status); data->status = AUTHOR_STATUS_ERROR; data->admin_msg = tac_strdup("Illegal return status from pre-authorization command"); data->msg = tac_strdup(error_str); data->num_out_args = 0; data->output_args = NULL; /* throw away out_args */ for (i = 0; i < out_cnt; i++) { free(out_args[i]); } if (out_args) { free(out_args); } return(1); case 0: /* Permit */ if (debug & DEBUG_AUTHOR_FLAG) report(LOG_DEBUG, "cmd %s returns 0 (unconditional permit)", cmd); data->status = AUTHOR_STATUS_PASS_ADD; data->num_out_args = 0; data->output_args = NULL; /* throw away out_args */ for (i = 0; i < out_cnt; i++) { free(out_args[i]); } if (out_args) { free(out_args); } return(1); case 1: /* Deny */ if (debug & DEBUG_AUTHOR_FLAG) report(LOG_DEBUG, "cmd %s returns %d (unconditional deny)", cmd, status); data->status = AUTHOR_STATUS_FAIL; data->msg = tac_strdup(error_str); data->num_out_args = 0; data->output_args = NULL; /* throw away out_args */ for (i = 0; i < out_cnt; i++) { free(out_args[i]); } if (out_args) { free(out_args); } return(1); case 2: /* Use replacement AV pairs from program as final result */ if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "cmd %s returns %d (permitted, args replaced)", cmd, status); for (i = 0; i < out_cnt; i++) report(LOG_DEBUG, "%s", out_args[i]); } /* and install the new set of AV pairs as output */ data->output_args = out_args; data->num_out_args = out_cnt; data->status = AUTHOR_STATUS_PASS_REPL; return(1); /* no more processing required */ case 3: /* deny, but return attributes and server-msg to NAS */ if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "cmd %s returns %d (deny, args replaced)", cmd, status); for (i = 0; i < out_cnt; i++) report(LOG_DEBUG, "%s", out_args[i]); } /* and install the new set of AV pairs as output */ data->output_args = out_args; data->num_out_args = out_cnt; data->msg = tac_strdup(error_str); data->status = AUTHOR_STATUS_FAIL; return(1); /* no more processing required */ } }
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); }
/* * Support for dollar variables. Look in the authorization data and return * strings representing values found there. If not found, return "unknown". * Recognized strings and their interpolated value types are: * * user -- user name * name -- NAS name * port -- NAS port * ip -- NAS ip * address -- NAC address (remote user location) * priv -- privilege level (0 to 15) * method -- (1 to 4) * type -- (1 to 4) * service -- (1 to 7) * status -- (pass, fail, error, unknown) */ static char * lookup(char *sym, struct author_data *data) { static char buf[5]; if (STREQ(sym, "user")) { return(tac_strdup(data->id->username)); } if (STREQ(sym, "name")) { return(tac_strdup(data->id->NAS_name)); } if (STREQ(sym, "ip")) { return(tac_strdup(data->id->NAS_ip)); } if (STREQ(sym, "port")) { return(tac_strdup(data->id->NAS_port)); } if (STREQ(sym, "address")) { return(tac_strdup(data->id->NAC_address)); } if (STREQ(sym, "priv")) { snprintf(buf, sizeof(buf), "%d", data->id->priv_lvl); return(tac_strdup(buf)); } if (STREQ(sym, "method")) { snprintf(buf, sizeof(buf), "%d", data->authen_method); return(tac_strdup(buf)); } if (STREQ(sym, "type")) { snprintf(buf, sizeof(buf), "%d", data->authen_type); return(tac_strdup(buf)); } if (STREQ(sym, "service")) { snprintf(buf, sizeof(buf), "%d", data->service); return(tac_strdup(buf)); } if (STREQ(sym, "status")) { switch (data->status) { default: return(tac_strdup("unknown")); case AUTHOR_STATUS_PASS_ADD: case AUTHOR_STATUS_PASS_REPL: return(tac_strdup("pass")); case AUTHOR_STATUS_FAIL: return(tac_strdup("fail")); case AUTHOR_STATUS_ERROR: return(tac_strdup("error")); } } return(tac_strdup("unknown")); }
/* * Cleartext password information has been requested. Look this up in * the config file. Set authen_data->status. * * Any strings pointed to by authen_data must come from the heap. They * will get freed by the caller. * * Return 0 if data->status is valid, otherwise 1 */ static int do_sendpass_fn(struct authen_data *data) { char *name; char *p; int expired; char *exp_date; char *secret; data->status = TAC_PLUS_AUTHEN_STATUS_FAIL; /* We must have a username */ if (!data->NAS_id->username[0]) { /* choose_authen should have already asked for a username, so this is * a gross error */ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR; data->server_msg = tac_strdup("No username supplied"); report(LOG_ERR, "%s: No username for sendpass_fn", session.peer); return(0); } name = data->NAS_id->username; exp_date = cfg_get_expires(name, TAC_PLUS_RECURSE); /* The user exists. Check the expiration date, if any */ expired = check_expiration(exp_date); switch (expired) { case PW_EXPIRED: data->status = TAC_PLUS_AUTHEN_STATUS_FAIL; data->server_msg = tac_strdup("Password has expired"); return(0); default: data->status = TAC_PLUS_AUTHEN_STATUS_ERROR; data->server_msg = tac_strdup("Bad return value for password " "expiration check"); report(LOG_ERR, "%s: Bogus return value %d from check_expiration", session.peer, expired); return(0); case PW_OK: case PW_EXPIRING: /* The user exists, and has not expired. Return her secret info */ switch (data->type) { case TAC_PLUS_AUTHEN_TYPE_CHAP: secret = cfg_get_chap_secret(name, TAC_PLUS_RECURSE); if (!secret) secret = cfg_get_global_secret(name, TAC_PLUS_RECURSE); break; #ifdef MSCHAP case TAC_PLUS_AUTHEN_TYPE_MSCHAP: secret = cfg_get_mschap_secret(name, TAC_PLUS_RECURSE); if (!secret) secret = cfg_get_global_secret(name, TAC_PLUS_RECURSE); break; #endif /* MSCHAP */ case TAC_PLUS_AUTHEN_TYPE_ARAP: secret = cfg_get_arap_secret(name, TAC_PLUS_RECURSE); if (!secret) secret = cfg_get_global_secret(name, TAC_PLUS_RECURSE); break; case TAC_PLUS_AUTHEN_TYPE_PAP: secret = cfg_get_opap_secret(name, TAC_PLUS_RECURSE); break; default: data->status = TAC_PLUS_AUTHEN_STATUS_ERROR; data->server_msg = tac_strdup("Illegal authentication type"); report(LOG_ERR, "%s: Illegal authentication type %d", session.peer, data->type); return(0); } if (!secret) { data->status = TAC_PLUS_AUTHEN_STATUS_FAIL; data->server_msg = tac_strdup("No secret"); return(0); } p = tac_find_substring("cleartext ", secret); if (!p) { /* Should never happen */ data->status = TAC_PLUS_AUTHEN_STATUS_ERROR; data->server_msg = tac_strdup("Illegal secret format"); report(LOG_ERR, "%s: Illegal secret format %s", session.peer, secret); return(0); } data->server_data = tac_strdup(p); data->server_dlen = strlen(data->server_data); data->status = TAC_PLUS_AUTHEN_STATUS_PASS; if (expired == PW_EXPIRING) { data->server_msg = tac_strdup("Secret will expire soon"); } return(0); } /* never reached */ }
int do_acct_file(struct acct_rec *rec) { int i, errors; time_t t = time(NULL); char ct[LINE_MAX]; struct tm *tm; tm = localtime(&t); strftime(ct, LINE_MAX, "%h %e %T", tm); if (!acctfd) { acctfd = open(session.acctfile, O_CREAT | O_WRONLY | O_APPEND, 0644); if (acctfd < 0) { report(LOG_ERR, "Can't open acct file %s -- %s", session.acctfile, strerror(errno)); return(1); } } if (!tac_lockfd(session.acctfile, acctfd)) { rec->admin_msg = tac_strdup("Cannot lock log file"); report(LOG_ERR, "%s: Cannot lock %s", session.peer, session.acctfile); return(1); } errors = 0; errors += acct_write(ct); errors += acct_write("\t"); errors += acct_write_field(rec->identity->NAS_name); errors += acct_write("\t"); errors += acct_write_field(rec->identity->username); errors += acct_write("\t"); errors += acct_write_field(rec->identity->NAS_port); errors += acct_write("\t"); errors += acct_write_field(rec->identity->NAC_address); errors += acct_write("\t"); switch(rec->acct_type) { case ACCT_TYPE_UPDATE: errors += acct_write("update\t"); break; case ACCT_TYPE_START: errors += acct_write("start\t"); break; case ACCT_TYPE_STOP: errors += acct_write("stop\t"); break; default: errors += acct_write("unknown\t"); break; } for (i=0; i < rec->num_args; i++) { errors += acct_write(rec->args[i]); if (i < (rec->num_args - 1)) errors += acct_write("\t"); } errors += acct_write("\n"); close(acctfd); acctfd = 0; if (errors) return(1); return(0); }
/* * 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); }
/* * We will eventually be called from inetd or via the rc scripts directly * Parse arguments and act appropiately. */ int main(int argc, char **argv) { extern char *optarg; FILE *fp; int c, *s, ns; struct pollfd *pfds; #if PROFILE moncontrol(0); #endif if ((progname = strrchr(*argv, '/')) != NULL) { progname++; } else progname = *argv; /* initialise global session data */ memset(&session, 0, sizeof(session)); session.peer = tac_strdup("unknown"); if (argc <= 1) { usage(); tac_exit(1); } while ((c = getopt(argc, argv, "B:C:d:hiPp:tGgvSsLw:u:")) != EOF) switch (c) { case 'B': /* bind() address*/ bind_address = optarg; break; case 'L': /* lookup peer names via DNS */ lookup_peer = 1; break; case 's': /* don't respond to sendpass */ sendauth_only = 1; break; case 'v': /* print version and exit */ vers(); tac_exit(1); case 't': console = 1; /* log to console too */ break; case 'P': /* Parse config file only */ parse_only = 1; break; case 'G': /* foreground */ opt_G = 1; break; case 'g': /* single threaded */ single = 1; break; case 'p': /* port */ port = atoi(optarg); portstr = optarg; break; case 'd': /* debug */ debug |= atoi(optarg); break; case 'C': /* config file name */ session.cfgfile = tac_strdup(optarg); break; case 'h': /* usage */ usage(); tac_exit(0); case 'i': /* inetd mode */ standalone = 0; break; case 'S': /* enable single-connection */ opt_S = 1; break; #ifdef MAXSESS case 'w': /* wholog file */ wholog = tac_strdup(optarg); break; #endif case 'u': wtmpfile = tac_strdup(optarg); break; default: fprintf(stderr, "%s: bad switch %c\n", progname, c); usage(); tac_exit(1); } parser_init(); /* read the configuration/etc */ init(); #if defined(REAPCHILD) && defined(REAPSIGIGN) client_count_init(); #endif open_logfile(); signal(SIGUSR1, handler); signal(SIGHUP, handler); signal(SIGUSR2, dump_clients_handler); signal(SIGTERM, die); signal(SIGPIPE, SIG_IGN); if (parse_only) tac_exit(0); if (debug) report(LOG_DEBUG, "tac_plus server %s starting", version); if (!standalone) { /* running under inetd */ char host[NI_MAXHOST]; int on; #ifdef IPV6 struct sockaddr_in6 name; #else struct sockaddr_in name; #endif socklen_t name_len; name_len = sizeof(name); session.flags |= SESS_NO_SINGLECONN; session.sock = 0; #ifdef IPV6 if (getpeername(session.sock, (struct sockaddr6 *)&name, &name_len)) { report(LOG_ERR, "getpeername failure %s", strerror(errno)); #else if (getpeername(session.sock, (struct sockaddr *)&name, &name_len)) { report(LOG_ERR, "getpeername failure %s", strerror(errno)); #endif } else { if (lookup_peer) on = 0; else on = NI_NUMERICHOST; #ifdef IPV6 if (getnameinfo((struct sockaddr6 *)&name, name_len, host, 128, NULL, 0, on)) { #else if (getnameinfo((struct sockaddr *)&name, name_len, host, 128, NULL, 0, on)) { #endif strncpy(host, "unknown", NI_MAXHOST - 1); host[NI_MAXHOST - 1] = '\0'; } if (session.peer) free(session.peer); session.peer = tac_strdup(host); if (session.peerip) free(session.peerip); #ifdef IPV6 session.peerip = tac_strdup((char *)inet_ntop(name.sin6_family, &name.sin6_addr, host, name_len)); #else session.peerip = tac_strdup((char *)inet_ntop(name.sin_family, &name.sin_addr, host, name_len)); #endif if (debug & DEBUG_AUTHEN_FLAG) report(LOG_INFO, "session.peerip is %s", session.peerip); } #ifdef FIONBIO on = 1; if (ioctl(session.sock, FIONBIO, &on) < 0) { report(LOG_ERR, "ioctl(FIONBIO) %s", strerror(errno)); tac_exit(1); } #endif start_session(); tac_exit(0); } if (single) { session.flags |= SESS_NO_SINGLECONN; } else { /* * Running standalone; background ourselves and release controlling * tty, unless -G option was specified to keep the parent in the * foreground. */ #ifdef SIGTTOU signal(SIGTTOU, SIG_IGN); #endif #ifdef SIGTTIN signal(SIGTTIN, SIG_IGN); #endif #ifdef SIGTSTP signal(SIGTSTP, SIG_IGN); #endif if (!opt_S) session.flags |= SESS_NO_SINGLECONN; if (!opt_G) { if ((childpid = fork()) < 0) report(LOG_ERR, "Can't fork first child"); else if (childpid > 0) exit(0); /* parent */ if (debug) report(LOG_DEBUG, "Backgrounded"); #if SETPGRP_VOID if (setpgrp() == -1) #else if (setpgrp(0, getpid()) == -1) #endif /* SETPGRP_VOID */ report(LOG_ERR, "Can't change process group: %s", strerror(errno)); /* XXX What does "REAPCHILD" have to do with TIOCNOTTY? */ #ifndef REAPCHILD c = open("/dev/tty", O_RDWR); if (c >= 0) { ioctl(c, TIOCNOTTY, (char *)0); (void) close(c); } #else /* REAPCHILD */ if ((childpid = fork()) < 0) report(LOG_ERR, "Can't fork second child"); else if (childpid > 0) exit(0); if (debug & DEBUG_FORK_FLAG) report(LOG_DEBUG, "Forked grandchild"); #endif /* REAPCHILD */ /* some systems require this */ closelog(); for (c = getdtablesize(); c >= 0; c--) (void)close(c); /* * make sure we can still log to syslog now that we have closed * everything */ open_logfile(); } } #if REAPCHILD #if REAPSIGIGN signal(SIGCHLD, reapchild); #else signal(SIGCHLD, SIG_IGN); #endif #endif ostream = NULL; /* chdir("/"); */ umask(022); errno = 0; get_socket(&s, &ns); #ifndef SOMAXCONN #define SOMAXCONN 5 #endif for (c = 0; c < ns; c++) { if (listen(s[c], SOMAXCONN) < 0) { console = 1; report(LOG_ERR, "listen: %s", strerror(errno)); tac_exit(1); } } if (port == TAC_PLUS_PORT) { if (bind_address == NULL) { strncpy(pidfilebuf, TACPLUS_PIDFILE, PIDSZ); if (pidfilebuf[PIDSZ - 1] != '\0') c = PIDSZ; else c = PIDSZ - 1; } else c = snprintf(pidfilebuf, PIDSZ, "%s.%s", TACPLUS_PIDFILE, bind_address); } else { if (bind_address == NULL) c = snprintf(pidfilebuf, PIDSZ, "%s.%d", TACPLUS_PIDFILE, port); else c = snprintf(pidfilebuf, PIDSZ, "%s.%s.%d", TACPLUS_PIDFILE, bind_address, port); } if (c >= PIDSZ) { pidfilebuf[PIDSZ - 1] = '\0'; report(LOG_ERR, "pid filename truncated: %s", pidfilebuf); childpid = 0; } else { /* write process id to pidfile */ if ((fp = fopen(pidfilebuf, "w")) != NULL) { fprintf(fp, "%d\n", (int)getpid()); fclose(fp); /* * After forking to disassociate; make sure we know we're the * mother so that we remove our pid file upon exit in die(). */ childpid = 1; } else { report(LOG_ERR, "Cannot write pid to %s %s", pidfilebuf, strerror(errno)); childpid = 0; } } #ifdef TACPLUS_GROUPID if (setgid(TACPLUS_GROUPID)) report(LOG_ERR, "Cannot set group id to %d %s", TACPLUS_GROUPID, strerror(errno)); #endif #ifdef TACPLUS_USERID if (setuid(TACPLUS_USERID)) report(LOG_ERR, "Cannot set user id to %d %s", TACPLUS_USERID, strerror(errno)); #endif #ifdef MAXSESS maxsess_loginit(); #endif /* MAXSESS */ report(LOG_DEBUG, "uid=%d euid=%d gid=%d egid=%d s=%d", getuid(), geteuid(), getgid(), getegid(), s); pfds = malloc(sizeof(struct pollfd) * ns); if (pfds == NULL) { report(LOG_ERR, "malloc failure: %s", strerror(errno)); tac_exit(1); } for (c = 0; c < ns; c++) { pfds[c].fd = s[c]; pfds[c].events = POLLIN | POLLERR | POLLHUP | POLLNVAL; } for (;;) { #if HAVE_PID_T pid_t pid; #else int pid; #endif char host[NI_MAXHOST]; #ifdef IPV6 struct sockaddr_in6 from; #else struct sockaddr_in from; #endif socklen_t from_len; int newsockfd, status; int flags; int procs_for_client; #if defined(REAPCHILD) && defined(REAPSIGIGN) if (reap_children) reapchildren(); #endif if (reinitialize) init(); if (dump_client_table) { report(LOG_ALERT, "Dumping Client Tables"); dump_client_tables(); dump_client_table = 0; } status = poll(pfds, ns, cfg_get_accepttimeout() * 1000); if (status == 0) continue; if (status == -1) if (errno == EINTR) continue; from_len = sizeof(from); memset((char *)&from, 0, from_len); for (c = 0; c < ns; c++) { if (pfds[c].revents & POLLIN) #ifdef IPV6 newsockfd = accept(s[c], (struct sockaddr6 *)&from, &from_len); #else newsockfd = accept(s[c], (struct sockaddr *)&from, &from_len); #endif else if (pfds[c].revents & (POLLERR | POLLHUP | POLLNVAL)) { report(LOG_ERR, "exception on listen FD %d", s[c]); tac_exit(1); } } if (newsockfd < 0) { if (errno == EINTR) continue; report(LOG_ERR, "accept: %s", strerror(errno)); continue; } if (lookup_peer) flags = 0; else flags = NI_NUMERICHOST; #ifdef IPV6 if (getnameinfo((struct sockaddr_in6 *)&from, from_len, host, 128, NULL, 0, flags)) { #else if (getnameinfo((struct sockaddr_in *)&from, from_len, host, 128, NULL, 0, flags)) { #endif strncpy(host, "unknown", NI_MAXHOST - 1); host[NI_MAXHOST - 1] = '\0'; } if (session.peer) free(session.peer); session.peer = tac_strdup(host); if (session.peerip) free(session.peerip); #ifdef IPV6 session.peerip = tac_strdup((char *)inet_ntop(from.sin6_family, &from.sin6_addr, host, INET6_ADDRSTRLEN)); #else session.peerip = tac_strdup((char *)inet_ntop(from.sin_family, &from.sin_addr, host, INET_ADDRSTRLEN)); #endif if (debug & DEBUG_PACKET_FLAG) report(LOG_DEBUG, "session request from %s sock=%d", session.peer, newsockfd); if (!single) { #if defined(REAPCHILD) && defined(REAPSIGIGN) /* first we check the tocal process count to see if we are at the limit */ if (total_child_count >= cfg_get_maxprocs()) { report(LOG_ALERT, "refused connection from %s [%s] at global max procs [%d]", session.peer, session.peerip, total_child_count); shutdown(newsockfd, 2); close(newsockfd); continue; } /* no we check the process count per client */ procs_for_client = get_client_count(session.peerip); report(LOG_ALERT, "connection [%d] from %s [%s]", procs_for_client + 1, session.peer, session.peerip); if (procs_for_client >= cfg_get_maxprocsperclt()) { report(LOG_ALERT, "refused connection from %s [%s] at client max procs [%d]", session.peer, session.peerip, procs_for_client); shutdown(newsockfd, 2); close(newsockfd); continue; } #endif pid = fork(); if (pid < 0) { report(LOG_ERR, "fork error"); tac_exit(1); } } else { pid = 0; } if (pid == 0) { /* child */ if (!single) { if (ns > 1) { for (c = 0; c < ns; c++) { close(s[c]); } } } session.sock = newsockfd; #ifdef LIBWRAP if (! hosts_ctl(progname,session.peer,session.peerip,progname)) { report(LOG_ALERT, "refused connection from %s [%s]", session.peer, session.peerip); shutdown(session.sock, 2); close(session.sock); if (!single) { tac_exit(0); } else { close(session.sock); continue; } } if (debug) report(LOG_DEBUG, "connect from %s [%s]", session.peer, session.peerip); #endif #if PROFILE moncontrol(1); #endif start_session(); shutdown(session.sock, 2); close(session.sock); if (!single) tac_exit(0); } else { /* parent */ #if defined(REAPCHILD) && defined(REAPSIGIGN) total_child_count++; procs_for_client = increment_client_count_for_proc(pid, session.peerip); snprintf(msgbuf, MSGBUFSZ, "forked %lu for %s, procs %d, procs for client %d", (long)pid, session.peerip, total_child_count, procs_for_client); report(LOG_DEBUG, msgbuf); #endif close(newsockfd); } } }