/* * Escape unprintable characters. */ static size_t linelog_escape_func(UNUSED REQUEST *request, char *out, size_t outlen, char const *in, UNUSED void *arg) { if (outlen == 0) return 0; if (outlen == 1) { *out = '\0'; return 0; } return fr_prints(out, outlen, in, -1, 0); }
/** Escape string that may contain binary data, and write it to a new buffer * * This is useful in situations where we expect printable strings as input, * but under some conditions may get binary data. A good example is libldap * and the arrays of struct berval ldap_get_values_len returns. * * @param[in] ctx To allocate new buffer in. * @param[in] in String to escape. * @param[in] inlen Length of string. Should be >= 0 if the data may contain * embedded \0s. Must be >= 0 if data may not be \0 terminated. * If < 0 inlen will be calculated using strlen. * @param[in] quote the quotation character. * @return new buffer holding the escaped string. */ char *fr_aprints(TALLOC_CTX *ctx, char const *in, ssize_t inlen, char quote) { size_t len, ret; char *out; len = fr_prints_len(in, inlen, quote); out = talloc_array(ctx, char, len); ret = fr_prints(out, len, in, inlen, quote); /* * This is a fatal error, but fr_assert is the strongest * assert we're allowed to use in library functions. */ if (!fr_assert(ret == (len - 1))) { talloc_free(out); return NULL; } return out; }
/* * Main program */ int main(int argc, char **argv) { CONF_SECTION *maincs, *cs; FILE *fp; struct radutmp rt; char othername[256]; char nasname[1024]; char session_id[sizeof(rt.session_id)+1]; int hideshell = 0; int showsid = 0; int rawoutput = 0; int radiusoutput = 0; /* Radius attributes */ char const *portind; int c; unsigned int portno; char buffer[2048]; char const *user = NULL; int user_cmp = 0; time_t now = 0; uint32_t nas_port = ~0; uint32_t nas_ip_address = INADDR_NONE; int zap = 0; raddb_dir = RADIUS_DIR; #ifndef NDEBUG if (fr_fault_setup(getenv("PANIC_ACTION"), argv[0]) < 0) { fr_perror("radwho"); exit(EXIT_FAILURE); } #endif talloc_set_log_stderr(); while((c = getopt(argc, argv, "d:D:fF:nN:sSipP:crRu:U:Z")) != EOF) switch (c) { case 'd': raddb_dir = optarg; break; case 'D': dict_dir = optarg; break; case 'F': radutmp_file = optarg; break; case 'h': usage(0); /* never returns */ case 'S': hideshell = 1; break; case 'n': showname = 0; break; case 'N': if (inet_pton(AF_INET, optarg, &nas_ip_address) < 0) { usage(1); } break; case 's': showname = 1; break; case 'i': showsid = 1; break; case 'p': showptype = 1; break; case 'P': nas_port = atoi(optarg); break; case 'c': showcid = 1; showname = 1; break; case 'r': rawoutput = 1; break; case 'R': radiusoutput = 1; now = time(NULL); break; case 'u': user = optarg; user_cmp = 0; break; case 'U': user = optarg; user_cmp = 1; break; case 'Z': zap = 1; break; default: usage(1); /* never returns */ } /* * Mismatch between the binary and the libraries it depends on */ if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) { fr_perror("radwho"); return 1; } if (dict_init(dict_dir, RADIUS_DICTIONARY) < 0) { fr_perror("radwho"); return 1; } if (dict_read(raddb_dir, RADIUS_DICTIONARY) == -1) { fr_perror("radwho"); return 1; } fr_strerror(); /* Clear the error buffer */ /* * Be safe. */ if (zap && !radiusoutput) zap = 0; /* * zap EVERYONE, but only on this nas */ if (zap && !user && (~nas_port == 0)) { /* * We need to know which NAS to zap users in. */ if (nas_ip_address == INADDR_NONE) usage(1); printf("Acct-Status-Type = Accounting-Off\n"); printf("NAS-IP-Address = %s\n", hostname(buffer, sizeof(buffer), nas_ip_address)); printf("Acct-Delay-Time = 0\n"); exit(0); /* don't bother printing anything else */ } if (radutmp_file) goto have_radutmp; /* * Initialize main_config */ memset(&main_config, 0, sizeof(main_config)); /* Read radiusd.conf */ maincs = cf_section_alloc(NULL, "main", NULL); if (!maincs) exit(1); snprintf(buffer, sizeof(buffer), "%.200s/radiusd.conf", raddb_dir); if (cf_file_read(maincs, buffer) < 0) { fprintf(stderr, "%s: Error reading or parsing radiusd.conf\n", argv[0]); talloc_free(maincs); exit(1); } cs = cf_section_sub_find(maincs, "modules"); if (!cs) { fprintf(stderr, "%s: No modules section found in radiusd.conf\n", argv[0]); exit(1); } /* Read the radutmp section of radiusd.conf */ cs = cf_section_sub_find_name2(cs, "radutmp", NULL); if (!cs) { fprintf(stderr, "%s: No configuration information in radutmp section of radiusd.conf\n", argv[0]); exit(1); } cf_section_parse(cs, NULL, module_config); /* Assign the correct path for the radutmp file */ radutmp_file = radutmpconfig.radutmp_fn; have_radutmp: if (showname < 0) showname = 1; /* * Show the users logged in on the terminal server(s). */ if ((fp = fopen(radutmp_file, "r")) == NULL) { fprintf(stderr, "%s: Error reading %s: %s\n", progname, radutmp_file, fr_syserror(errno)); return 0; } /* * Don't print the headers if raw or RADIUS */ if (!rawoutput && !radiusoutput) { fputs(showname ? hdr1 : hdr2, stdout); fputs(eol, stdout); } /* * Read the file, printing out active entries. */ while (fread(&rt, sizeof(rt), 1, fp) == 1) { char name[sizeof(rt.login) + 1]; if (rt.type != P_LOGIN) continue; /* hide logout sessions */ /* * We don't show shell users if we are * fingerd, as we have done that above. */ if (hideshell && !strchr("PCS", rt.proto)) continue; /* * Print out sessions only for the given user. */ if (user) { /* only for a particular user */ if (((user_cmp == 0) && (strncasecmp(rt.login, user, strlen(user)) != 0)) || ((user_cmp == 1) && (strncmp(rt.login, user, strlen(user)) != 0))) { continue; } } /* * Print out only for the given NAS port. */ if (~nas_port != 0) { if (rt.nas_port != nas_port) continue; } /* * Print out only for the given NAS IP address */ if (nas_ip_address != INADDR_NONE) { if (rt.nas_address != nas_ip_address) continue; } memcpy(session_id, rt.session_id, sizeof(rt.session_id)); session_id[sizeof(rt.session_id)] = 0; if (!rawoutput && rt.nas_port > (showname ? 999 : 99999)) { portind = ">"; portno = (showname ? 999 : 99999); } else { portind = "S"; portno = rt.nas_port; } /* * Print output as RADIUS attributes */ if (radiusoutput) { memcpy(nasname, rt.login, sizeof(rt.login)); nasname[sizeof(rt.login)] = '\0'; fr_prints(buffer, sizeof(buffer), nasname, -1, '"'); printf("User-Name = \"%s\"\n", buffer); fr_prints(buffer, sizeof(buffer), session_id, -1, '"'); printf("Acct-Session-Id = \"%s\"\n", buffer); if (zap) printf("Acct-Status-Type = Stop\n"); printf("NAS-IP-Address = %s\n", hostname(buffer, sizeof(buffer), rt.nas_address)); printf("NAS-Port = %u\n", rt.nas_port); switch (rt.proto) { case 'S': printf("Service-Type = Framed-User\n"); printf("Framed-Protocol = SLIP\n"); break; case 'P': printf("Service-Type = Framed-User\n"); printf("Framed-Protocol = PPP\n"); break; default: printf("Service-type = Login-User\n"); break; } if (rt.framed_address != INADDR_NONE) { printf("Framed-IP-Address = %s\n", hostname(buffer, sizeof(buffer), rt.framed_address)); } /* * Some sanity checks on the time */ if ((rt.time <= now) && (now - rt.time) <= (86400 * 365)) { printf("Acct-Session-Time = %" PRId64 "\n", (int64_t) (now - rt.time)); } if (rt.caller_id[0] != '\0') { memcpy(nasname, rt.caller_id, sizeof(rt.caller_id)); nasname[sizeof(rt.caller_id)] = '\0'; fr_prints(buffer, sizeof(buffer), nasname, -1, '"'); printf("Calling-Station-Id = \"%s\"\n", buffer); } printf("\n"); /* separate entries with a blank line */ continue; } /* * Show the fill name, or not. */ memcpy(name, rt.login, sizeof(rt.login)); name[sizeof(rt.login)] = '\0'; if (showname) { if (rawoutput == 0) { printf("%-10.10s %-17.17s %-5.5s %s%-3u %-9.9s %-15.15s %-.19s%s", name, showcid ? rt.caller_id : (showsid? session_id : fullname(rt.login)), proto(rt.proto, rt.porttype), portind, portno, dotime(rt.time), hostname(nasname, sizeof(nasname), rt.nas_address), hostname(othername, sizeof(othername), rt.framed_address), eol); } else { printf("%s,%s,%s,%s%u,%s,%s,%s%s", name, showcid ? rt.caller_id : (showsid? session_id : fullname(rt.login)), proto(rt.proto, rt.porttype), portind, portno, dotime(rt.time), hostname(nasname, sizeof(nasname), rt.nas_address), hostname(othername, sizeof(othername), rt.framed_address), eol); } } else { if (rawoutput == 0) { printf("%-10.10s %s%-5u %-6.6s %-13.13s %-15.15s %-.28s%s", name, portind, portno, proto(rt.proto, rt.porttype), dotime(rt.time), hostname(nasname, sizeof(nasname), rt.nas_address), hostname(othername, sizeof(othername), rt.framed_address), eol); } else { printf("%s,%s%u,%s,%s,%s,%s%s", name, portind, portno, proto(rt.proto, rt.porttype), dotime(rt.time), hostname(nasname, sizeof(nasname), rt.nas_address), hostname(othername, sizeof(othername), rt.framed_address), eol); } } } fclose(fp); return 0; }
/* * Make sure user/pass are clean * and then log them */ static int rad_authlog(char const *msg, REQUEST *request, int goodpass) { int logit; char const *extra_msg = NULL; char clean_password[1024]; char clean_username[1024]; char buf[1024]; char extra[1024]; char *p; VALUE_PAIR *username = NULL; if (!request->root->log_auth) { return 0; } /* * Get the correct username based on the configured value */ if (!log_stripped_names) { username = pairfind(request->packet->vps, PW_USER_NAME, 0, TAG_ANY); } else { username = request->username; } /* * Clean up the username */ if (username == NULL) { strcpy(clean_username, "<no User-Name attribute>"); } else { fr_prints(clean_username, sizeof(clean_username), username->vp_strvalue, username->vp_length, '\0'); } /* * Clean up the password */ if (request->root->log_auth_badpass || request->root->log_auth_goodpass) { if (!request->password) { VALUE_PAIR *auth_type; auth_type = pairfind(request->config, PW_AUTH_TYPE, 0, TAG_ANY); if (auth_type) { snprintf(clean_password, sizeof(clean_password), "<via Auth-Type = %s>", dict_valnamebyattr(PW_AUTH_TYPE, 0, auth_type->vp_integer)); } else { strcpy(clean_password, "<no User-Password attribute>"); } } else if (pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) { strcpy(clean_password, "<CHAP-Password>"); } else { fr_prints(clean_password, sizeof(clean_password), request->password->vp_strvalue, request->password->vp_length, '\0'); } } if (goodpass) { logit = request->root->log_auth_goodpass; extra_msg = request->root->auth_goodpass_msg; } else { logit = request->root->log_auth_badpass; extra_msg = request->root->auth_badpass_msg; } if (extra_msg) { extra[0] = ' '; p = extra + 1; if (radius_xlat(p, sizeof(extra) - 1, request, extra_msg, NULL, NULL) < 0) { return -1; } } else { *extra = '\0'; } RAUTH("%s: [%s%s%s] (%s)%s", msg, clean_username, logit ? "/" : "", logit ? clean_password : "", auth_name(buf, sizeof(buf), request, 1), extra); return 0; }
/** Find the length of the buffer required to fully escape a string with fr_prints * * Were assuming here that's it's cheaper to figure out the length and do one * alloc than repeatedly expand the buffer when we find extra chars which need * to be added. * * @param in string to calculate the escaped length for. * @param inlen length of the input string, if < 0 strlen will be used to check the length. * @param[in] quote the quotation character. * @return the size of buffer required to hold the escaped string including the NUL byte. */ size_t fr_prints_len(char const *in, ssize_t inlen, char quote) { return fr_prints(NULL, 0, in, inlen, quote) + 1; }
/** Allocates a stats template which describes a single guage/counter * * This is just intended to simplify allocating a fairly complex memory structure * src and dst pointers must be set * * @param ctx Context to allocate collectd struct in. * @param conf Radsniff configuration. * @param plugin_instance usually the type of packet (in our case). * @param type string, the name of a collection of stats e.g. exchange * @param type_instance the name of the counter/guage within the collection e.g. latency. * @param stats structure to derive statistics from. * @param values Value templates used to populate lcc_value_list. * @return a new rs_stats_tmpl_t on success or NULL on failure. */ static rs_stats_tmpl_t *rs_stats_collectd_init(TALLOC_CTX *ctx, rs_t *conf, char const *plugin_instance, char const *type, char const *type_instance, void *stats, rs_stats_value_tmpl_t const *values) { static char hostname[255]; static char fqdn[LCC_NAME_LEN]; size_t len; int i; char *p; rs_stats_tmpl_t *tmpl; lcc_value_list_t *value; assert(conf); assert(type); assert(type_instance); for (len = 0; values[len].src; len++) {} ; assert(len > 0); /* * Initialise hostname once so we don't call gethostname every time */ if (*fqdn == '\0') { int ret; struct addrinfo hints, *info = NULL; if (gethostname(hostname, sizeof(hostname)) < 0) { ERROR("Error getting hostname: %s", fr_syserror(errno)); return NULL; } memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; /*either IPV4 or IPV6*/ hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_CANONNAME; if ((ret = getaddrinfo(hostname, "radius", &hints, &info)) != 0) { ERROR("Error getting hostname: %s", gai_strerror(ret)); return NULL; } strlcpy(fqdn, info->ai_canonname, sizeof(fqdn)); freeaddrinfo(info); } tmpl = talloc_zero(ctx, rs_stats_tmpl_t); if (!tmpl) { return NULL; } tmpl->value_tmpl = talloc_zero_array(tmpl, rs_stats_value_tmpl_t, len); if (!tmpl->value_tmpl) { goto error; } tmpl->stats = stats; value = talloc_zero(tmpl, lcc_value_list_t); if (!value) { goto error; } tmpl->value = value; value->interval = conf->stats.interval; value->values_len = len; value->values_types = talloc_zero_array(value, int, len); if (!value->values_types) { goto error; } value->values = talloc_zero_array(value, value_t, len); if (!value->values) { goto error; } for (i = 0; i < (int) len; i++) { assert(values[i].src); assert(values[i].cb); tmpl->value_tmpl[i] = values[i]; switch (tmpl->value_tmpl[i].type) { case LCC_TYPE_COUNTER: tmpl->value_tmpl[i].dst = &value->values[i].counter; break; case LCC_TYPE_GAUGE: tmpl->value_tmpl[i].dst = &value->values[i].gauge; break; case LCC_TYPE_DERIVE: tmpl->value_tmpl[i].dst = &value->values[i].derive; break; case LCC_TYPE_ABSOLUTE: tmpl->value_tmpl[i].dst = &value->values[i].absolute; break; default: assert(0); } value->values_types[i] = tmpl->value_tmpl[i].type; } /* * These should be OK as is */ strlcpy(value->identifier.host, fqdn, sizeof(value->identifier.host)); /* * Plugin is ASCII only and no '/' */ fr_prints(value->identifier.plugin, sizeof(value->identifier.plugin), conf->stats.prefix, strlen(conf->stats.prefix), '\0'); for (p = value->identifier.plugin; *p; ++p) { if ((*p == '-') || (*p == '/'))*p = '_'; } /* * Plugin instance is ASCII only (assuming printable only) and no '/' */ fr_prints(value->identifier.plugin_instance, sizeof(value->identifier.plugin_instance), plugin_instance, strlen(plugin_instance), '\0'); for (p = value->identifier.plugin_instance; *p; ++p) { if ((*p == '-') || (*p == '/')) *p = '_'; } /* * Type is ASCII only (assuming printable only) and no '/' or '-' */ fr_prints(value->identifier.type, sizeof(value->identifier.type), type, strlen(type), '\0'); for (p = value->identifier.type; *p; ++p) { if ((*p == '-') || (*p == '/')) *p = '_'; } fr_prints(value->identifier.type_instance, sizeof(value->identifier.type_instance), type_instance, strlen(type_instance), '\0'); for (p = value->identifier.type_instance; *p; ++p) { if ((*p == '-') || (*p == '/')) *p = '_'; } return tmpl; error: talloc_free(tmpl); return NULL; }