ootp/urd/urd.c

1023 lines
25 KiB
C

/*
* 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 <sys/types.h>
#include <sys/socket.h>
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <netdb.h>
#include <errno.h>
#include <inttypes.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#if HAVE_STRINGS_H
#include <strings.h>
#endif
#if HAVE_STRING_H
#include <string.h>
#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 */