/* * Copyright (c) 2009 Mark Fullmer * Copyright (c) 2009 Mark Fullmer and the Ohio State University * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: urd.c 50 2009-12-15 01:37:19Z maf $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #if HAVE_STRINGS_H #include #endif #if HAVE_STRING_H #include #endif #ifdef OOTP_ENABLE #include "otplib.h" #include "ffdb.h" #endif /* OOTP_ENABLE */ #include "pw.h" #include "rad.h" #include "xerr.h" /* * XXX * urd_rep_msg in access-challenge hard coded to ABC.. * copy proxy variables into reply packet per RFC? * packet stress testing */ static void usage(void); static int server_secret_load(char *fname, char *buf, int buf_len); static u_long scan_ip(char *s); static int write_pidfile(char *fname); int main(int argc, char **argv) { struct sockaddr_in loc_addr; struct urd_ctx *urdctx; struct pass_db_ctx *pdbctx; #ifdef OOTP_ENABLE struct otp_ctx *otpctx; struct otp_user ou; char *otpdb_fname; int otp_skip_unknown, otpdb_flags, otp_enable; #endif /* OOTP_ENABLE */ fd_set rfd; u_long tmpul; uint64_t rep_state; uint32_t local_ip; uint16_t local_port; uint8_t rep_code; uint rem_addr_len; char *authorized_users_fname, *pwfile_fname, *server_secret_fname, *endptr; char server_secret[URD_SECRET_LEN+1], buf[1024], *pid_fname; int rep_enc_flags, rep_cache_flags, debug, daemon_mode; int drop, drop_mode, req_cache_hit, buf_l, pkt_fd, r, i, otp_window; bzero(&loc_addr, sizeof loc_addr); bzero(&pkt_fd, sizeof pkt_fd); bzero(&rfd, sizeof rfd); debug = 0; daemon_mode = 1; authorized_users_fname = "/var/urd/authorized_users"; pwfile_fname = "/var/urd/passwd"; server_secret_fname = "/var/urd/server_secret"; pid_fname = (char*)0L; local_ip = INADDR_ANY; local_port = URD_PORT; drop = 1; drop_mode = 0; otp_window = OTP_WINDOW_DEFAULT; #ifdef OOTP_ENABLE otpctx = (struct otp_ctx*)0L; otpdb_fname = OTP_DB_FNAME; otp_skip_unknown = 0; otpdb_flags = 0; otp_enable = 1; #endif /* OOTP_ENABLE */ xerr_setid(argv[0]); #ifdef OOTP_ENABLE while ((i = getopt(argc, argv, "AhduDOx?a:b:B:o:p:s:P:w:")) != -1) { #else while ((i = getopt(argc, argv, "AhdDx?a:b:B:p:s:P:w:")) != -1) { #endif /* OOTP_ENABLE */ switch (i) { case 'a': authorized_users_fname = optarg; break; case 'A': authorized_users_fname = (char*)0L; break; case 'b': if (!(local_ip = scan_ip(optarg))) xerr_errx(1, "scan_ip(%s): fatal", optarg); break; case 'B': tmpul = strtoul(optarg, &endptr, 0); if (*endptr) xerr_errx(1, "stroul(%s): failed at %c.", optarg, *endptr); if (tmpul > 0xFFFF) xerr_errx(1, "UDP port out of range 0..65535."); local_port = tmpul; break; case 'd': debug ++; #ifdef OOTP_ENABLE otpdb_flags |= OTP_DB_VERBOSE; #endif /* OOTP_ENABLE */ break; case 'D': daemon_mode = 0; break; case 'h': case '"': usage(); exit(0); break; #ifdef OOTP_ENABLE case 'o': otpdb_fname = optarg; break; case 'O': otp_enable = 0; break; #endif /* OOTP_ENABLE */ case 'p': pwfile_fname = optarg; break; case 'P': pid_fname = optarg; break; case 's': server_secret_fname = optarg; break; #ifdef OOTP_ENABLE case 'u': otp_skip_unknown = 1; break; #endif /* OOTP_ENABLE */ case 'w': tmpul = strtoul(optarg, &endptr, 0); if (*endptr) xerr_errx(1, "stroul(%s): failed at %c.", optarg, *endptr); if (tmpul > OTP_WINDOW_MAX) xerr_errx(1, "Challenge window %lu > %lu.", tmpul, OTP_WINDOW_MAX); otp_window = tmpul; break; case 'x': drop_mode = 1; break; } /* switch */ } /* while getopt() */ if (daemon_mode) { xerr_setsyslog2(1); /* use syslog for output */ /* run in the background */ if (daemon(0, 0) < 0) xerr_err(1, "dameon()"); } /* daemon_mode */ buf_l = snprintf(buf, 1024, "urd start:"); if (debug) { for (i = 0; i < argc; ++i) buf_l += snprintf(buf+buf_l, 1024-buf_l, " %s", argv[i]); } xerr_info(buf); /* init to recv UDP datagrams */ loc_addr.sin_family = AF_INET; loc_addr.sin_addr.s_addr = htonl(local_ip); loc_addr.sin_port = htons(local_port); /* construct pid file name */ if (!pid_fname) { if (local_ip || (local_port != URD_PORT)) { buf_l = snprintf(buf, 1024, "/var/urd/pid.%s.%d", inet_ntoa(loc_addr.sin_addr), (int)local_port); } else { buf_l = snprintf(buf, 1024, "/var/urd/pid"); } pid_fname = (char*)&buf; } /* write out pidfile */ if (write_pidfile(pid_fname) < 0) xerr_errx(1, "write_pidfile(%s): fatal", buf); /* load shared server secret */ if (server_secret_load(server_secret_fname, server_secret, URD_SECRET_LEN+1) < 0) xerr_errx(1, "server_secret_load(%s): fatal", server_secret_fname); /* setup password database */ if (!(pdbctx = pass_db_ctx_new(pwfile_fname, authorized_users_fname))) xerr_errx(1, "pass_db_ctx_new(%s,%s): fatal", pwfile_fname, authorized_users_fname); pass_db_debug(pdbctx, debug); /* load password database */ if (pass_db_load(pdbctx) < 0) xerr_errx(1, "pass_db_load(): fatal"); if (debug) pass_db_stats(pdbctx); /* socket to receive URD datagrams */ if ((pkt_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) xerr_err(1, "socket()"); #ifdef OOTP_ENABLE /* creat OTP context */ if (otp_enable) if (!(otpctx = otp_db_open(otpdb_fname, otpdb_flags))) xerr_errx(1, "otp_db_open(%s): failed", otpdb_fname); #endif /* OOTP_ENABLE */ /* create radius context */ if (!(urdctx = urd_ctx_new(server_secret))) xerr_errx(1, "urd_ctx_new(): fatal"); urd_debug(urdctx, debug); /* bind socket to address */ if (bind(pkt_fd, (struct sockaddr*)&loc_addr, sizeof(loc_addr)) < 0) xerr_err(1, "bind(%s)", inet_ntoa(loc_addr.sin_addr)); while (1) { /* grab a datagram */ rem_addr_len = sizeof (urdctx->req.rem_addr); if ((urdctx->req.pkt_len = recvfrom(pkt_fd, &urdctx->req.pkt_buf, (size_t)URD_MAX_DGRAM_LEN, 0, (struct sockaddr*)&urdctx->req.rem_addr, &rem_addr_len)) < 0) { xerr_warn("recvfrom()"); continue; } /* * Handle two types of authentication requests, those with state, and * those without. Auth requests with state are used to tie the * OTP phase of a two phase authentication (reusable password, OTP) * together into a session. * * A request cache is kept with the code and state used for a reply * This is required for OTP to work robustly in an environment where * the client may have lost the reply from the server and retransmits * (OTP will only work once). * * Requests with state which can not be tied to the initial * request without state (ie the reusable password phase) are * dropped. * * The request cache and state cache are unified with a hash * table for each. Cache entries are stored in a fixed size * ring buffer. * * request with no state: * req_cache lookup: * hit : * rep_code and state_counter from cache * miss : * rep_code based on pw_auth and otp requirements: * access-reject: (no state, failed password auth) * access-accept: (no state, user does not need OTP) * access-challenge: (increment state) * reply: * reject: don't encode state or rep_msg * access-challenge: encode state and rep_msg * access-accept: don't encode state or rep_msg * * request with state: * req_cache lookup: * hit : * rep_code and state_counter from cache * rep_msg from cache XXX future * miss : * state_cache lookup : * hit : * rep_code based on otp_auth: * access-reject: encode state, no rep_msg * access-accept: encode state, no rep_msg * access-challenge: not permitted here * miss : * bogus, no previous state from pw_auth phase, reject, log * * reply: * reject: encode state, no rep_msg * access-accept: encode state, no rep_msg * access-challenge: not permitted here * */ /* reply constructed from cache? */ req_cache_hit = 0; /* decode/sanity check request */ if ((r = urd_req_decode(urdctx)) < 0) { xerr_warnx("urd_req_decode(): failed"); continue; } /* non fatal packet decode xerr_error, ignore it */ if (r > 1) continue; if (debug) urd_req_dump(urdctx); /* RADIUS ACCESS-REQUEST? */ if (urdctx->req.dgram_header.code == RADIUS_CODE_ACCESS_REQUEST) { if (debug) xerr_info("req: ACCESS-REQUEST"); /* check for minimum TLV's in packet */ if ((!urdctx->req.tlv_User_Name) || (!urdctx->req.tlv_User_Password) || ((!urdctx->req.tlv_NAS_IP_Address) && (!urdctx->req.tlv_NAS_Identifier))) { xerr_warnx("req: min required fields missing"); continue; } req_cache_hit = 0; rep_code = RADIUS_CODE_ACCESS_REJECT; rep_state = 0LL; rep_enc_flags = 0; rep_cache_flags = 0; /* initial no state access request */ if (!urdctx->req.tlv_State) { if (debug) xerr_info("req: (no state)"); /* first check the cache for a previously composed reply */ if ((r = urd_req_cache_lookup(urdctx, &rep_code, &rep_state)) < 0) xerr_errx(1, "urd_req_cache_lookup(): fatal"); /* hit, then skip to response gen */ if (r == URD_REQ_CACHE_HIT) { req_cache_hit = 1; if ((rep_code == RADIUS_CODE_ACCESS_REJECT) || (rep_code == RADIUS_CODE_ACCESS_ACCEPT)) { rep_enc_flags = 0x0; } else if (rep_code == RADIUS_CODE_ACCESS_CHALLENGE) { rep_enc_flags = URD_ENCODE_FLAG_STATE|URD_ENCODE_FLAG_MSG; } if (debug) xerr_info("req: cache hit"); goto access_request_rep; } else { if (debug) xerr_info("req: cache miss"); } /* default to reject */ rep_code = RADIUS_CODE_ACCESS_REJECT; /* check password database */ if ((r = pass_db_auth(pdbctx, urdctx->req.user_name, urdctx->req.user_pass)) < 0) xerr_errx(1, "pass_db_auth(): fatal"); /* valid password? */ if (r != PASS_DB_AUTH_SUCCESS) { if (debug) xerr_info("req: fail auth via pass_db_auth() "); goto access_request_rep; } else { if (debug) xerr_info("req: pass auth via pass_db_auth() "); } /* special urd_stats user? */ if (!strcmp(urdctx->req.user_name, "urd_stats")) { urd_state_cache_stats(urdctx); urd_req_cache_stats(urdctx); goto access_request_rep; } /* special urd_debug user? */ if (!strcmp(urdctx->req.user_name, "urd_debug")) { if (debug) { debug = 0; urd_debug(urdctx, 0); } else { debug = 1; urd_debug(urdctx, 1); } goto access_request_rep; } #ifndef OOTP_ENABLE /* * without one time passwords compiled in, no additional auth * checks are required, reply with ACCEPT */ rep_code = RADIUS_CODE_ACCESS_ACCEPT; rep_enc_flags = 0x0; if (debug) xerr_info("req: ACCEPT (nothing more to do)."); goto access_request_rep; #else /* OOTP_ENABLE */ /* * if one time passwords are disabled, then the password * check above meets auth requirements, reply with ACCEPT * */ if (!otp_enable) { rep_code = RADIUS_CODE_ACCESS_ACCEPT; rep_enc_flags = 0x0; if (debug) xerr_info("req: ACCEPT with OTP disabled."); goto access_request_rep; } /* * check OTP */ if ((r = otp_user_exists(otpctx, urdctx->req.user_name)) < 0) xerr_errx(1, "otp_user_exists(): fail."); /* if user does not exist and not okay to skip OTP users then fail */ if ((r == 1) && (otp_skip_unknown == 0)) { if (debug) xerr_info("req: fail via otp_user_exists() skip_unknown=0"); goto access_request_rep; } if (otp_urec_open(otpctx, urdctx->req.user_name, &ou, O_RDONLY, FFDB_OP_LOCK_EX) < 0) xerr_errx(1, "otp_urec_open(%s): failed.", urdctx->req.user_name); if (otp_urec_get(otpctx, &ou) < 0) xerr_errx(1, "otp_urec_get(): failed."); if (otp_urec_close(otpctx, &ou) < 0) xerr_errx(1, "otp_urec_close(): failed."); /* disabled user is rejected */ if (ou.status == OTP_STATUS_DISABLED) { if (debug) xerr_info("req: fail via otp_user_get() (disabled)"); goto access_request_rep; } /* inactive user does not need OTP */ if (ou.status == OTP_STATUS_INACTIVE) { /* reply with an ACCEPT, no state, no challenge message */ rep_code = RADIUS_CODE_ACCESS_ACCEPT; rep_enc_flags = 0x0; if (debug) xerr_info("req: pass via otp_user_get() (inactive)"); goto access_request_rep; } /* verify count not at ceiling */ if (ou.count >= ou.count_ceil) { if (debug) xerr_info("req: fail ou.count >= ou.count_ceil."); goto access_request_rep; } /* * made it this far then user is in authorized_users database, * reusable password is valid in password database, and user * exists in the OTP database in an active state */ if (ou.status == OTP_STATUS_ACTIVE) { /* reply with challenge, challenge message, new state */ rep_code = RADIUS_CODE_ACCESS_CHALLENGE; rep_enc_flags = URD_ENCODE_FLAG_STATE|URD_ENCODE_FLAG_MSG; rep_state = ++urdctx->state_counter; rep_cache_flags = URD_CACHE_FLAG_STATE; if (debug) xerr_info("req: pass via otp_user_get() (active)"); goto access_request_rep; } #endif /* OOTP_ENABLE */ } else if (urdctx->req.tlv_State) { #ifndef OOTP_ENABLE /* * without one time passwords compiled in, a request with * state will always return REJECT. * */ /* reply with inbound state */ rep_state = urdctx->req.state_counter; /* default to reject */ rep_code = RADIUS_CODE_ACCESS_REJECT; if (debug) xerr_info("req: state set, nothing to do, REJECT."); goto access_request_rep; #else /* OOTP_ENABLE */ /* request has state, always reply with it */ rep_enc_flags = URD_ENCODE_FLAG_STATE; if (debug) xerr_info("req: (state)"); if ((r = urd_req_cache_lookup(urdctx, &rep_code, &rep_state)) < 0) xerr_errx(1, "urd_req_cache_lookup(): fatal"); /* hit, then skip to response gen */ if (r == URD_REQ_CACHE_HIT) { req_cache_hit = 1; if (debug) xerr_info("req: cache hit"); goto access_request_rep; } else { if (debug) xerr_info("req: cache miss"); } /* reply with inbound state */ rep_state = urdctx->req.state_counter; /* default to reject */ rep_code = RADIUS_CODE_ACCESS_REJECT; /* * state cache lookup -- user must have previously authenticated * successfully to continue */ if ((r = urd_state_cache_lookup(urdctx, &rep_code)) < 0) xerr_errx(1, "urd_state_cache_lookup(): fatal"); /* if no hit then can not continue */ if (r != URD_REQ_CACHE_HIT) { if (debug) xerr_info("state: cache miss (user not validated with passwd)"); goto access_request_rep; } else { if (debug) xerr_info("state: cache hit"); } /* double check code */ if (rep_code != RADIUS_CODE_ACCESS_CHALLENGE) { if (debug) xerr_warnx("req: fail cache'd state rep_code"); goto access_request_rep; } /* * default to reject. State cache lookup will have CHALLENGE * code as it is shared with the request cache. */ rep_code = RADIUS_CODE_ACCESS_REJECT; if ((r = otp_user_exists(otpctx, urdctx->req.user_name)) < 0) xerr_errx(1, "otp_user_exists(): fail."); /* user exist?, if not failure */ /* note, initial access request checked this */ if (r != 0) { if (debug) xerr_info("req: fail via otp_user_exists()"); goto access_request_rep; } if (otp_urec_open(otpctx, urdctx->req.user_name, &ou, O_RDONLY, FFDB_OP_LOCK_EX) < 0) xerr_errx(1, "otp_urec_open(%s): failed.", urdctx->req.user_name); if (otp_urec_get(otpctx, &ou) < 0) xerr_errx(1, "otp_urec_get(): failed."); if (otp_urec_close(otpctx, &ou) < 0) xerr_errx(1, "otp_urec_close(): failed."); /* note, initial access request checked this */ if (ou.status != OTP_STATUS_ACTIVE) { if (debug) xerr_info("req: fail via otp_user_get() (not ACTIVE)"); goto access_request_rep; } /* verify count not at ceiling */ /* note, initial access request checked this */ if (ou.count >= ou.count_ceil) { if (debug) xerr_info("req: fail ou.count >= ou.count_ceil."); goto access_request_rep; } if ((r = otp_user_auth(otpctx, urdctx->req.user_name, urdctx->req.user_pass, otp_window)) < 0) xerr_errx(1, "otp_user_auth(): failed."); if (r == OTP_AUTH_PASS) { rep_code = RADIUS_CODE_ACCESS_ACCEPT; if (debug) xerr_info("req: pass via otp_user_auth()"); goto access_request_rep; } else { if (debug) xerr_info("req: fail via otp_user_auth()"); } /* failed authentication... */ #endif /* OOTP_ENABLE */ } else { xerr_info("unsupported code=%2.2X", (int)urdctx->req.dgram_header.code); } /* type of request */ /* reply to request */ access_request_rep: if (debug) { if (rep_code == RADIUS_CODE_ACCESS_ACCEPT) xerr_info("rep: ACCESS-ACCEPT"); else if (rep_code == RADIUS_CODE_ACCESS_REJECT) xerr_info("rep: ACCESS-REJECT"); else if (rep_code == RADIUS_CODE_ACCESS_CHALLENGE) xerr_info("rep: ACCESS-CHALLENGE"); } /* debug */ /* * construct reply */ if (urd_rep_encode(urdctx, rep_code, rep_state, rep_enc_flags) < 0) { xerr_errx(1, "urd_rep_encode(): fatal"); } /* update reply cache */ if (urd_req_cache_update(urdctx, rep_code, rep_state, rep_cache_flags) < 0) xerr_errx(1, "urd_req_cache_update(): fatal"); /* debugging / drop every other packet */ if (drop_mode) { if (drop) { drop = 0; continue; /* skip packet send */ } else { drop = 1; } } if (sendto(pkt_fd, &urdctx->rep.pkt_buf, urdctx->rep.pkt_len, 0, (struct sockaddr*) &urdctx->rep.rem_addr, sizeof(urdctx->rep.rem_addr)) < 0) { xerr_err(1, "sendto()"); } } /* ACCESS_REQUEST */ } /* forever */ /* not reached */ /* cleanup */ pass_db_ctx_free(pdbctx); #ifdef OOTP_ENABLE otp_db_close(otpctx); #endif /* OOTP_ENABLE */ } /* main */ /* * function: scan_ip * * IP address in string S is converted to a u_long * (borrowed from tcpdump) * * left shift any partial dotted quads, ie 10 is 0x0a000000 not 0x0a * so scan_ip_prefix() works for standard prefix notation, ie 10/8 */ u_long scan_ip(char *s) { struct hostent *he; struct in_addr *ina; u_long addr = 0; uint n; int dns, shift; char *t; /* if there is anything ascii in here, this may be a hostname */ for (dns = 0, t = s; *t; ++t) { if (islower((int)*t) || isupper((int)*t)) { dns = 1; break; } } if (dns) { if (!(he = gethostbyname(s))) goto numeric; if (he->h_addrtype != AF_INET) goto numeric; if (he->h_length != sizeof (uint32_t)) goto numeric; ina = (struct in_addr*)*he->h_addr_list; return (ntohl(ina->s_addr)); } /* dns */ shift = 0; numeric: while (1) { /* n is the nibble */ n = 0; /* nibble's are . bounded */ while (*s && (*s != '.') && (*s != ' ') && (*s != '\t')) n = n * 10 + *s++ - '0'; /* shift in the nibble */ addr <<=8; addr |= n & 0xff; ++shift; /* return on end of string */ if ((!*s) || (*s == ' ') || (*s == '\t')) goto ndone; /* skip the . */ ++s; } /* forever */ ndone: for (; shift < 4; ++shift) addr <<=8; return addr; } /* scan_ip */ /* * function: server_secret_load() * * load a one line secret (password) from fname into buf of length buf_len * secret is assumed to be ASCII and will be null terminated * * returns: < 0 : fail * 0 : success */ int server_secret_load(char *fname, char *buf, int buf_len) { struct stat sb; int fd, len, ret, i; fd = -1; ret = -1; /* open secret file */ if ((fd = open(fname, O_RDONLY, 0)) < 0) { xerr_warn("open(%s)", fname); goto server_secret_load_out; } /* grab size */ if (fstat(fd, &sb) < 0) { xerr_warn("stat(%s)", fname); goto server_secret_load_out; } if ((sb.st_size+1) >= buf_len) { xerr_warn("secret buffer too small"); goto server_secret_load_out; } if ((len = read(fd, buf, sb.st_size)) < 0) { xerr_warn("read(%s)", fname); goto server_secret_load_out; } if (len != sb.st_size) { xerr_warnx("short read(%s)", fname); goto server_secret_load_out; } /* strip \n */ for (i = 0; i < sb.st_size; ++i) if (buf[i] == '\n') buf[i] = 0; /* ensure null termination */ buf[sb.st_size] = 0; /* success */ ret = 0; server_secret_load_out: if (fd != -1) close(fd); return ret; } /* server_secret_load */ /* * function: write_pidfile() * * Store proces ID in ASII to fname. * * returns: < 0 : fail * 0 : success */ int write_pidfile(char *fname) { int fd, buf_l; char buf[512]; buf_l = snprintf(buf, 512, "%lu\n", (unsigned long)getpid()); if ((fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0 ) { xerr_warn("open(%s)", fname); return -1; } if (write(fd, buf, buf_l) < 0) { xerr_warn("write(%s)", fname); close (fd); return -1; } return (close(fd)); } /* write_pidfile */ /* * function: usage() * */ void usage(void) { #ifdef OOTP_ENABLE fprintf(stderr, "urd [-AhdDOux?] [-a allowed_users_file] [-b local_ip] [-B local_port ]\n"); fprintf(stderr, " [-o otp_db] [-p passwd_file] [-P pid_file] [-s secret_file]\n"); #else fprintf(stderr, "urd [-AhdDx?] [-a allowed_users_file] [-b local_ip] [-B local_port ]\n"); fprintf(stderr, " [-p passwd_file] [-P pid_file] [-s secret_file]\n"); #endif /* OOTP_ENABLE */ fprintf(stderr, " [-w otp_window]\n\n"); fprintf(stderr, " -A disable authorized_users file (all users in passwd_file valid)\n"); fprintf(stderr, " -h help\n"); fprintf(stderr, " -d enable debugging\n"); fprintf(stderr, " -D disable daemon mode\n"); #ifdef OOTP_ENABLE fprintf(stderr, " -O disable one time passwords\n"); fprintf(stderr, " -u allow users which do not exist in OTP database\n"); #endif /* OOTP_ENABLE */ fprintf(stderr, " -x drop alternate replies (debugging)\n"); } /* help */