/* * Copyright (c) 2005 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: otp-control.c 179 2011-05-16 02:48:42Z maf $ */ #include #include #include #include #include #include #include #include "otplib.h" #include "otpsc.h" #include "xerr.h" #include "ffdb.h" #include "str.h" #define MODE_ADD 1 #define MODE_DUMP 5 #define MODE_GENERATE 6 #define MODE_LIST 7 #define MODE_LOAD 8 #define MODE_REMOVE 9 #define MODE_SET_COUNT 10 #define MODE_SET_COUNT_CEIL 11 #define MODE_TEST 12 #define MODE_CREATE 13 #define MODE_SEND_TOKEN 14 #define MODE_LIST_SC 18 #define MODE_SET_STATUS 19 #define MODE_SET_TYPE 20 #define MODE_SET_FORMAT 21 #define MODE_SET_FLAGS 22 #define MODE_SET_LOCATION 23 #define KEY_HEX160_LEN 40 #define DEV_RANDOM "/dev/urandom" int get_random(char *dev, unsigned char *entropy, int bits); void help(void); int main (int argc, char **argv) { extern char *ootp_version; struct otp_ctx *otpctx; struct otp_user ou; int i, j, r, mode, window, db_flags, open_mode, open_op, verbose; int opt_version, u_count_set; char *otpdb_fname; uint64_t u_count, u_count_ceil, count_offset, tmp64u; uint8_t u_version, u_status, u_format, u_type, u_flags, sc_index; uint8_t sc_flags[SC_HOSTNAME_LEN]; unsigned char u_key160[20]; char key_hex160[KEY_HEX160_LEN+1]; char crsp_tmp[11]; char *u_username, *u_loc, *u_key_ascii, *sc_hostname; char *endptr, *i_status, *i_format, *i_type, *i_flags, *service; struct option longopts[] = { { "count", 1, (void*)0L, 'c'}, { "count-ceil", 1, (void*)0L, 'C'}, { "format", 1, (void*)0L, 'f'}, { "flags", 1, (void*)0L, 'F'}, { "sc-hostname", 1, (void*)0L, 'H'}, { "sc-index", 1, (void*)0L, 'I'}, { "key", 1, (void*)0L, 'k'}, { "location", 1, (void*)0L, 'l'}, { "command-mode", 1, (void*)0L, 'm'}, { "otp-db", 1, (void*)0L, 'o'}, { "status", 1, (void*)0L, 's'}, { "sc-flags", 1, (void*)0L, 'S'}, { "type", 1, (void*)0L, 't'}, { "username", 1, (void*)0L, 'u'}, { "challenge-window", 1, (void*)0L, 'w'}, { "help", 0, (void*)0L, 'h'}, { "create-database", 0, (void*)0L, 'n'}, { "verbose", 0, (void*)0L, 'v'}, { "service-name", 0, (void*)0L, 'V'}, { "version", 0, &opt_version, 1}, { 0, 0, 0, 0}, }; otpdb_fname = OTP_DB_FNAME; sc_index = 0; mode = 0; window = 1; verbose = 0; u_count_set = 0; db_flags = 0; /* user defaults */ u_count = 1; u_count_ceil = 0xFFFFFFFFFFFFFFFFLL; u_version = OTP_VERSION; u_format = OTP_FORMAT_HEX40; u_type = OTP_TYPE_HOTP; u_status = OTP_STATUS_ACTIVE; u_flags = 0; u_username = (char*)0L; u_key_ascii = (char*)0L; u_loc = (char*)0L; endptr = (char*)0L; sc_hostname = (char*)0L; service = "otp-control"; bzero(sc_flags, SC_HOSTNAME_LEN); i_status = i_type = i_format = i_flags = (char*)0L; opt_version = 0; /* init xerr */ xerr_setid(argv[0]); while ((i = getopt_long(argc, argv, "c:C:hf:F:H:I:?k:l:m:no:s:S:t:u:w:vV:", longopts, (int*)0L)) != -1) { switch (i) { case 'c': u_count = strtoull(optarg, &endptr, 0); if (*endptr) xerr_errx(1, "strtoull(%s): failed at %c.", optarg, *endptr); u_count_set = 1; break; case 'C': u_count_ceil = strtoull(optarg, &endptr, 0); if (*endptr) xerr_errx(1, "strtoull(%s): failed at %c.", optarg, *endptr); break; case 'f': i_format = optarg; break; case 'F': if (str_setflag8(otp_flags_l, &u_flags, optarg, 0, OTP_FLAGS_BITS) < 0) xerr_errx(1, "Invalid flag %s.", optarg); break; case 'H': sc_hostname = optarg; if (strlen(sc_hostname) > SC_HOSTNAME_LEN) xerr_errx(1, "strlen(sc_hostname) > SC_HOSTNAME_LEN"); break; case 'I': tmp64u = strtoull(optarg, &endptr, 0); if (*endptr) xerr_errx(1, "strtoull(%s): failed at %c.", optarg, *endptr); if (tmp64u > SC_INDEX_MAX) xerr_errx(1, "sc_index > SC_INDEX_MAX."); sc_index = tmp64u; break; case 'k': u_key_ascii = optarg; break; case 'l': u_loc = optarg; if (strlen(u_loc) > OTP_USER_LOC_LEN) xerr_errx(1, "u_loc > OTP_USER_LOC_LEN"); break; case 'm': if (mode) xerr_errx(1, "mode previously set."); if (!strcasecmp(optarg, "add")) { mode = MODE_ADD; } else if (!strcasecmp(optarg, "create")) { mode = MODE_CREATE; } else if (!strcasecmp(optarg, "dump")) { mode = MODE_DUMP; } else if (!strcasecmp(optarg, "generate")) { mode = MODE_GENERATE; } else if (!strcasecmp(optarg, "list")) { mode = MODE_LIST; } else if (!strcasecmp(optarg, "list-sc")) { mode = MODE_LIST_SC; } else if (!strcasecmp(optarg, "load")) { mode = MODE_LOAD; } else if (!strcasecmp(optarg, "remove")) { mode = MODE_REMOVE; } else if (!strcasecmp(optarg, "send-token")) { mode = MODE_SEND_TOKEN; } else if (!strcasecmp(optarg, "set-count")) { mode = MODE_SET_COUNT; } else if (!strcasecmp(optarg, "set-count-ceil")) { mode = MODE_SET_COUNT_CEIL; } else if (!strcasecmp(optarg, "set-flags")) { mode = MODE_SET_FLAGS; } else if (!strcasecmp(optarg, "set-format")) { mode = MODE_SET_FORMAT; } else if (!strcasecmp(optarg, "set-location")) { mode = MODE_SET_LOCATION; } else if (!strcasecmp(optarg, "set-status")) { mode = MODE_SET_STATUS; } else if (!strcasecmp(optarg, "set-type")) { mode = MODE_SET_TYPE; } else if (!strcasecmp(optarg, "test")) { mode = MODE_TEST; } else { xerr_errx(1, "Unknown mode %s.", optarg); } break; case 's': i_status = optarg; break; case 'S' : for (j = 0; j < strlen(optarg); ++j) { if (optarg[j] == '0') sc_flags[HOSTNAME_POS_CHALLENGE] = HOSTNAME_FLAG_MASK; else if (optarg[j] == '1') sc_flags[HOSTNAME_POS_READERKEY] = HOSTNAME_FLAG_MASK; else xerr_errx(1, "Unknown sc_flag %c.", optarg[j]); } /* j */ break; case 't': i_type = optarg; break; case 'n': db_flags |= OTP_DB_CREATE_SOFT; break; case 'o': otpdb_fname = optarg; break; case 'u': u_username = optarg; break; case 'v': db_flags |= OTP_DB_VERBOSE; verbose = 1; break; case 'V': service = optarg; break; case 'h': case '?': help(); exit(0); case 'w': window = atoi(optarg); break; case 0: if (opt_version) { printf("%s\n", ootp_version); exit(0); } break; default: xerr_errx(1, "getopt_long(): fatal."); break; /* not reached */ } /* switch */ } /* while */ if (!mode) xerr_errx(1, "No mode set."); /* username required for most modes */ if ((mode != MODE_DUMP) && (mode != MODE_LOAD) && (mode != MODE_CREATE)) { if ((!u_username) || (!u_username[0])) { xerr_errx(1, "Username required."); } } /* smart card hostname field required for MODE_LIST_SC */ if ((mode == MODE_LIST_SC) && (!sc_hostname)) xerr_errx(1, "Hostname required."); /* check username length */ if (u_username && (strlen(u_username) > OTP_USER_NAME_LEN)) xerr_errx(1, "Username > OTP_USER_NAME_LEN."); /* input key */ if (u_key_ascii && u_key_ascii[0] != '-') xerr_errx(1, "Key not accepted on command line, use - for stdin"); /* format */ if (i_format) if (str_find8(otp_format_l, &u_format, i_format, 1, OTP_FORMAT_MAX)) xerr_errx(1, "Invalid format %s.", i_format); if ((mode == MODE_SET_FORMAT) && (!i_format)) xerr_errx(1, "Format value not specified."); /* status */ if (i_status) if (str_find8(otp_status_l, &u_status, i_status, 1, OTP_STATUS_MAX)) xerr_errx(1, "Invalid status %s.", i_status); if ((mode == MODE_SET_STATUS) && (!i_status)) xerr_errx(1, "Status value not specified."); if ((mode == MODE_SET_LOCATION) && (!u_loc)) xerr_errx(1, "Location value not specified."); /* type */ if (i_type) if (str_find8(otp_type_l, &u_type, i_type, 1, OTP_TYPE_MAX)) xerr_errx(1, "Invalid type %s.", i_type); if ((mode == MODE_SET_TYPE) && (!i_type)) xerr_errx(1, "Type value not specified."); /* user specified key? need key material? */ if (mode == MODE_ADD) { /* key not on command line (safe) */ if (u_key_ascii && (u_key_ascii[0] == '-') && (u_key_ascii[1] == 0)) { if (str_input("160 bit shared key (hex): ", key_hex160, KEY_HEX160_LEN+1, STR_FLAGS_ECHO_OFF) < 0) xerr_errx(1, "str_input(160): failed."); if (strlen(key_hex160) != 40) xerr_errx(1, "Key failure, expecting 40 hex digits."); if (str_hex_decode(key_hex160, 40, u_key160, 20) == -1) xerr_errx(1, "str_hex_decode(%s): failed.", key_hex160); } else { if (verbose) printf("Generating random 160 bit key.\n"); if (get_random(DEV_RANDOM, u_key160, 160) < 0) xerr_errx(1, "get_random(): failed."); } } /* MODE_ADD */ if (mode == MODE_CREATE) { db_flags |= OTP_DB_CREATE; } if (!(otpctx = otp_db_open(otpdb_fname, db_flags))) { xerr_errx(1, "otp_db_open(): failed."); } if (mode == MODE_CREATE) { printf("Created db %s.\n", otpdb_fname); goto mode_done; } if (mode == MODE_DUMP) { if (otp_db_dump(otpctx, u_username) < 0) xerr_errx(1, "otp_db_dump(): failed."); goto mode_done; } /* MODE_DUMP */ if (mode == MODE_LOAD) { if (otp_db_load(otpctx, u_username) < 0) xerr_errx(1, "otp_db_load(): failed."); goto mode_done; } /* MODE_LOAD */ if (mode == MODE_ADD) { printf("Adding user %s.\n", u_username); if (otp_user_add(otpctx, u_username, u_loc, u_key160, OTP_HOTP_KEY_SIZE, u_count, u_count_ceil, u_status, u_type, u_format, u_version)) xerr_errx(1, "otp_user_add(): failed."); goto mode_done; } /* MODE_ADD */ if (mode == MODE_REMOVE) { printf("Removing user %s.\n", u_username); if (otp_user_rm(otpctx, u_username) < 0) xerr_errx(1, "otp_user_rm(): failed."); goto mode_done; } /* MODE_REMOVE */ if (mode == MODE_SEND_TOKEN) { if (otp_user_send_token(otpctx, u_username, service) < 0) xerr_errx(1, "otp_user_send_token(): failed."); goto mode_done; } /* MODE_SEND_TOKEN */ /* * modes requiring open and get of user record: */ if ((mode == MODE_GENERATE) || (mode == MODE_LIST) || (mode == MODE_LIST_SC) || (mode == MODE_SET_COUNT) || (mode == MODE_SET_COUNT_CEIL) || (mode == MODE_SET_LOCATION) || (mode == MODE_SET_FLAGS) || (mode == MODE_SET_FORMAT) || (mode == MODE_SET_STATUS) || (mode == MODE_SET_TYPE) || (mode == MODE_TEST)) { /* rw or ro? */ if ((mode == MODE_LIST) || (mode==MODE_LIST_SC)) { open_mode = O_RDONLY; open_op = FFDB_OP_LOCK_SH; } else { open_mode = O_RDWR; open_op = FFDB_OP_LOCK_EX; } if (otp_urec_open(otpctx, u_username, &ou, open_mode, open_op) < 0) xerr_errx(1, "otp_urec_open(%s): failed.", u_username); if (otp_urec_get(otpctx, &ou) < 0) xerr_errx(1, "otp_urec_open(%s): failed.", u_username); } /* open & get urec */ if (mode == MODE_TEST) { if (otp_urec_close(otpctx, &ou) < 0) xerr_errx(1, "otp_urec_close(): failed."); printf("Testing authentication for user %s.\n", u_username); printf("OTP challenge for user %s (%" PRIu64 "): ", u_username, ou.count); fflush(stdout); scanf("%10s", crsp_tmp); r = otp_user_auth(otpctx, u_username, crsp_tmp, window); if (r < 0) xerr_errx(1, "otp_user_auth(): failed."); if (r == OTP_AUTH_PASS) printf("Success.\n"); else if (r == OTP_AUTH_FAIL) printf("Fail.\n"); else xerr_errx(1, "otp_user_auth(): unknown response."); goto mode_done; } /* MODE_TEST */ if (mode == MODE_GENERATE) { for (count_offset = 0; count_offset < window; ++count_offset) { if (u_count_set == 1) ou.count = u_count; if (otp_urec_crsp(otpctx, &ou, count_offset, crsp_tmp, 11) < 0) xerr_errx(1, "otp_urec_crsp(): failed."); printf("count=%" PRIu64 " crsp=%s\n", ou.count+count_offset, crsp_tmp); } goto mode_close; } /* MODE_GENERATE */ if ((mode == MODE_SET_COUNT) || (mode == MODE_SET_COUNT_CEIL) || (mode == MODE_SET_FLAGS) || (mode == MODE_SET_LOCATION) || (mode == MODE_SET_FORMAT) || (mode == MODE_SET_STATUS) || (mode == MODE_SET_TYPE)) { if (mode == MODE_SET_COUNT) ou.count = u_count; else if (mode == MODE_SET_COUNT_CEIL) ou.count_ceil = u_count_ceil; else if (mode == MODE_SET_FLAGS) ou.flags = u_flags; else if (mode == MODE_SET_FORMAT) ou.format = u_format; else if (mode == MODE_SET_STATUS) ou.status = u_status; else if (mode == MODE_SET_TYPE) ou.type = u_type; else if (mode == MODE_SET_LOCATION) { strncpy(ou.loc, u_loc, sizeof(ou.loc)); ou.loc[sizeof(ou.loc)-1] = 0; } if (otp_urec_put(otpctx, &ou) < 0) xerr_errx(1, "otp_urec_put(): failed."); goto mode_close; } /* update user record mode */ if (mode == MODE_LIST) { otp_urec_disp(otpctx, &ou); goto mode_close; } /* MODE_LIST */ if (mode == MODE_LIST_SC) { printf("#index:count:hostname:key\n"); otp_urec_dispsc(otpctx, &ou, sc_index, sc_hostname, sc_flags); goto mode_close; } /* MODE_LIST_SC */ mode_close: if (otp_urec_close(otpctx, &ou) < 0) xerr_errx(1, "otp_urec_close(): failed."); mode_done: return 0; } /* main */ void help(void) { extern char *ootp_version; int i; fprintf(stderr, "otp-control [-?hnv] [-c count] [-C count_ceil] [-f format] [-F flag]\n"); fprintf(stderr, " [-H sc_hostname] [-I sc_index] [-k key] [-l location]\n"); fprintf(stderr, " [-m command_mode] [-o otbdb_pathname] [-s status] [-S sc_flags] [-V service]\n"); fprintf(stderr, " [-t type] [-u username] [-w window]\n"); fprintf(stderr, " -h : help\n"); fprintf(stderr, " -n : create database\n"); fprintf(stderr, " -v : enable verbose output\n"); fprintf(stderr, " --version : build version\n\n"); fprintf(stderr, " sc_flags : 0=CHALLENGE 1=READERKEY\n"); fprintf(stderr, " flags : "); for (i = 0; i < OTP_FLAGS_BITS; ++i) fprintf(stderr, "%s ", otp_flags_l[i]); fprintf(stderr, "\n"); fprintf(stderr, " format list : "); for (i = 1; i <= OTP_FORMAT_MAX; ++i) fprintf(stderr, "%s ", otp_format_l[i]); fprintf(stderr, "\n"); fprintf(stderr, " type list : "); for (i = 1; i <= OTP_TYPE_MAX; ++i) fprintf(stderr, "%s ", otp_type_l[i]); fprintf(stderr, "\n"); fprintf(stderr, " status list : "); for (i = 1; i <= OTP_STATUS_MAX; ++i) fprintf(stderr, "%s ", otp_status_l[i]); fprintf(stderr, "\n"); fprintf(stderr, "\n"); fprintf(stderr, " Mode Description\n"); fprintf(stderr, " -------------------------------------------------\n"); fprintf(stderr, " add - Add user\n"); fprintf(stderr, " create - Create database\n"); fprintf(stderr, " dump - ASCII dump user record(s)\n"); fprintf(stderr, " generate - Generate HOTP for user\n"); fprintf(stderr, " list - List user record (printable)\n"); fprintf(stderr, " list-sc - List user record (SC friendly)\n"); fprintf(stderr, " load - ASCII load user record(s)\n"); fprintf(stderr, " remove - Remove user\n"); fprintf(stderr, " send-token - Send token to user\n"); fprintf(stderr, " set-count - Set user count\n"); fprintf(stderr, " set-count-ceil - Set user count ceiling\n"); fprintf(stderr, " set-flags - Set user flags\n"); fprintf(stderr, " set-format - Set user format\n"); fprintf(stderr, " set-status - Set user status\n"); fprintf(stderr, " set-type - Set user OTP type\n"); fprintf(stderr, " test - Test user\n"); printf("%s\n", ootp_version); } int get_random(char *dev, unsigned char *entropy, int bits) { int fd; int bytes; bytes = bits / 8; if ((fd = open(dev, O_RDONLY, 0)) < 0) { fprintf(stderr, "open(%s): %s\n", dev, strerror(errno)); return -1; } if (read(fd, entropy, bytes) != bytes) { fprintf(stderr, "read(%s): failed to gather %d bytes of entropy.\n", dev, bytes); close(fd); return -1; } close(fd); return 0; } /* get_random */