/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int doorfd = -1; /* * These are additional return values for the command line parsing * function (parsecmd()). They are specific to this utility, but * need to share the same space as the IKE_SVC_* defs, without conflicts. * So they're defined relative to the end of that range. */ #define IKEADM_HELP_GENERAL IKE_SVC_MAX + 1 #define IKEADM_HELP_GET IKE_SVC_MAX + 2 #define IKEADM_HELP_SET IKE_SVC_MAX + 3 #define IKEADM_HELP_ADD IKE_SVC_MAX + 4 #define IKEADM_HELP_DEL IKE_SVC_MAX + 5 #define IKEADM_HELP_DUMP IKE_SVC_MAX + 6 #define IKEADM_HELP_FLUSH IKE_SVC_MAX + 7 #define IKEADM_HELP_READ IKE_SVC_MAX + 8 #define IKEADM_HELP_WRITE IKE_SVC_MAX + 9 #define IKEADM_HELP_TOKEN IKE_SVC_MAX + 10 #define IKEADM_HELP_HELP IKE_SVC_MAX + 11 #define IKEADM_EXIT IKE_SVC_MAX + 12 /* * Disable default TAB completion for now (until some brave soul tackles it). */ /* ARGSUSED */ static CPL_MATCH_FN(no_match) { return (0); } static void command_complete(int s) __NORETURN; static void usage(void) __NORETURN; static void command_complete(int s) { if (interactive) { longjmp(env, 1); } else { exit(s); } } static void usage(void) { if (!interactive) { (void) fprintf(stderr, gettext("Usage:\t" "ikeadm [ -hnp ] cmd obj [cmd-specific options]\n")); (void) fprintf(stderr, gettext(" \tikeadm help\n")); } else { (void) fprintf(stderr, gettext("\nType help for usage info\n")); } command_complete(1); } static void print_help() { (void) printf(gettext("Valid commands and objects:\n")); (void) printf( "\tget debug|priv|stats|p1|rule|preshared|defaults [%s]\n", gettext("identifier")); (void) printf("\tset priv %s\n", gettext("level")); (void) printf("\tset debug %s [%s]\n", gettext("level"), gettext("filename")); (void) printf("\tadd rule|preshared {%s}|%s\n", gettext("definition"), gettext("filename")); (void) printf("\tdel p1|rule|preshared %s\n", gettext("identifier")); (void) printf("\tdump p1|rule|preshared|certcache|groups|" "encralgs|authalgs\n"); (void) printf("\tflush p1|certcache\n"); (void) printf("\tread rule|preshared [%s]\n", gettext("filename")); (void) printf("\twrite rule|preshared %s\n", gettext("filename")); (void) printf("\ttoken %s\n", gettext("")); (void) printf( "\thelp [get|set|add|del|dump|flush|read|write|token|help]\n"); (void) printf("\texit %s\n", gettext("exit the program")); (void) printf("\tquit %s\n", gettext("exit the program")); command_complete(0); } static void print_get_help() { (void) printf( gettext("This command gets information from in.iked.\n\n")); (void) printf(gettext("Objects that may be retrieved include:\n")); (void) printf("\tdebug\t\t"); (void) printf(gettext("the current debug level\n")); (void) printf("\tpriv\t\t"); (void) printf(gettext("the current privilege level\n")); (void) printf("\tstats\t\t"); (void) printf(gettext("current usage statistics\n")); (void) printf("\tp1\t\t"); (void) printf(gettext("a phase 1 SA, identified by\n")); (void) printf(gettext("\t\t\t local_ip remote_ip OR\n")); (void) printf(gettext("\t\t\t init_cookie resp_cookie\n")); (void) printf("\trule\t\t"); (void) printf(gettext("a phase 1 rule, identified by its label\n")); (void) printf("\tpreshared\t"); (void) printf(gettext("a preshared key, identified by\n")); (void) printf(gettext("\t\t\t local_ip remote_ip OR\n")); (void) printf(gettext("\t\t\t local_id remote_id\n")); (void) printf("\n"); command_complete(0); } static void print_set_help() { (void) printf(gettext("This command sets values in in.iked.\n\n")); (void) printf(gettext("Objects that may be set include:\n")); (void) printf("\tdebug\t\t"); (void) printf(gettext("change the debug level\n")); (void) printf("\tpriv\t\t"); (void) printf( gettext("change the privilege level (may only be lowered)\n")); (void) printf("\n"); command_complete(0); } static void print_add_help() { (void) printf( gettext("This command adds items to in.iked's tables.\n\n")); (void) printf(gettext("Objects that may be set include:\n")); (void) printf("\trule\t\t"); (void) printf(gettext("a phase 1 policy rule\n")); (void) printf("\tpreshared\t"); (void) printf(gettext("a preshared key\n")); (void) printf( gettext("\nObjects may be entered on the command-line, as a\n")); (void) printf( gettext("series of keywords and tokens contained in curly\n")); (void) printf( gettext("braces ('{', '}'); or the name of a file containing\n")); (void) printf(gettext("the object definition may be provided.\n\n")); (void) printf( gettext("For security purposes, preshared keys may only be\n")); (void) printf( gettext("entered on the command-line if ikeadm is running in\n")); (void) printf(gettext("interactive mode.\n")); (void) printf("\n"); command_complete(0); } static void print_del_help() { (void) printf( gettext("This command deletes an item from in.iked's tables.\n\n")); (void) printf(gettext("Objects that may be deleted include:\n")); (void) printf("\tp1\t\t"); (void) printf(gettext("a phase 1 SA, identified by\n")); (void) printf(gettext("\t\t\t local_ip remote_ip OR\n")); (void) printf(gettext("\t\t\t init_cookie resp_cookie\n")); (void) printf("\trule\t\t"); (void) printf(gettext("a phase 1 rule, identified by its label\n")); (void) printf("\tpreshared\t"); (void) printf(gettext("a preshared key, identified by\n")); (void) printf(gettext("\t\t\t local_ip remote_ip OR\n")); (void) printf(gettext("\t\t\t local_id remote_id\n")); (void) printf("\n"); command_complete(0); } static void print_dump_help() { (void) printf( gettext("This command dumps one of in.iked's tables.\n\n")); (void) printf(gettext("Tables that may be dumped include:\n")); (void) printf("\tp1\t\t"); (void) printf(gettext("all phase 1 SAs\n")); (void) printf("\trule\t\t"); (void) printf(gettext("all phase 1 rules\n")); (void) printf("\tpreshared\t"); (void) printf(gettext("all preshared keys\n")); (void) printf("\tcertcache\t"); (void) printf(gettext("all cached certificates\n")); (void) printf("\tgroups\t\t"); (void) printf(gettext("all implemented Diffie-Hellman groups\n")); (void) printf("\tencralgs\t"); (void) printf(gettext("all encryption algorithms for IKE\n")); (void) printf("\tauthalgs\t"); (void) printf(gettext("all authentication algorithms IKE\n")); (void) printf("\n"); command_complete(0); } static void print_flush_help() { (void) printf( gettext("This command clears one of in.iked's tables.\n\n")); (void) printf(gettext("Tables that may be flushed include:\n")); (void) printf("\tp1\t\t"); (void) printf(gettext("all phase 1 SAs\n")); (void) printf("\tcertcache\t"); (void) printf(gettext("all cached certificates\n")); (void) printf("\n"); command_complete(0); } static void print_read_help() { (void) printf( gettext("This command reads a new configuration file into\n")); (void) printf( gettext("in.iked, discarding the old configuration info.\n\n")); (void) printf(gettext("Sets of data that may be read include:\n")); (void) printf("\trule\t\t"); (void) printf(gettext("all phase 1 rules\n")); (void) printf("\tpreshared\t"); (void) printf(gettext("all preshared keys\n\n")); (void) printf( gettext("A filename may be provided to specify a source file\n")); (void) printf(gettext("other than the default.\n")); (void) printf("\n"); command_complete(0); } static void print_write_help() { (void) printf( gettext("This command writes in.iked's current configuration\n")); (void) printf(gettext("out to a config file.\n\n")); (void) printf(gettext("Sets of data that may be written include:\n")); (void) printf("\trule\t\t"); (void) printf(gettext("all phase 1 rules\n")); (void) printf("\tpreshared\t"); (void) printf(gettext("all preshared keys\n\n")); (void) printf( gettext("A filename must be provided to specify the file to\n")); (void) printf(gettext("which the information should be written.\n")); (void) printf("\n"); command_complete(0); } static void print_token_help() { (void) printf(gettext( "This command logs IKE into and out of PKCS#11 tokens.\n\n")); (void) printf(gettext("Commands include:\n")); (void) printf("\tlogin \t"); (void) printf(gettext("log into token\n")); (void) printf("\tlogout \t"); (void) printf(gettext("log out of token\n\n")); (void) printf( gettext("The PKCS#11 Token Object name must be " "enclosed in quotation marks.\n")); (void) printf("\n"); command_complete(0); } static void print_help_help() { (void) printf( gettext("This command provides information about commands.\n\n")); (void) printf( gettext("The 'help' command alone provides a list of valid\n")); (void) printf( gettext("commands, along with the valid objects for each.\n")); (void) printf( gettext("'help' followed by a valid command name provides\n")); (void) printf(gettext("further information about that command.\n")); (void) printf("\n"); command_complete(0); } /*PRINTFLIKE1*/ static void message(char *fmt, ...) { va_list ap; char msgbuf[BUFSIZ]; va_start(ap, fmt); (void) vsnprintf(msgbuf, BUFSIZ, fmt, ap); (void) fprintf(stderr, gettext("ikeadm: %s\n"), msgbuf); va_end(ap); } static int open_door(void) { if (doorfd >= 0) (void) close(doorfd); doorfd = open(DOORNM, O_RDONLY); return (doorfd); } static ike_service_t * ikedoor_call(char *reqp, int size, door_desc_t *descp, int ndesc) { door_arg_t arg; int retries = 0; arg.data_ptr = reqp; arg.data_size = size; arg.desc_ptr = descp; arg.desc_num = ndesc; arg.rbuf = (char *)NULL; arg.rsize = 0; retry: if (door_call(doorfd, &arg) < 0) { if ((errno == EBADF) && ((++retries < 2) && (open_door() >= 0))) goto retry; (void) fprintf(stderr, gettext("Unable to communicate with in.iked\n")); Bail("door_call failed"); } if ((ndesc > 0) && (descp->d_attributes & DOOR_RELEASE) && ((errno == EBADF) || (errno == EFAULT))) { /* callers assume passed fds will be closed no matter what */ (void) close(descp->d_data.d_desc.d_descriptor); } /* LINTED E_BAD_PTR_CAST_ALIGN */ return ((ike_service_t *)arg.rbuf); } /* * Parsing functions */ /* stolen from ipseckey.c, with a second tier added */ static int parsecmd(char *cmdstr, char *objstr) { #define MAXOBJS 11 struct objtbl { char *obj; int token; }; static struct cmdtbl { char *cmd; int null_obj_token; struct objtbl objt[MAXOBJS]; } table[] = { {"get", IKE_SVC_ERROR, { {"debug", IKE_SVC_GET_DBG}, {"priv", IKE_SVC_GET_PRIV}, {"stats", IKE_SVC_GET_STATS}, {"p1", IKE_SVC_GET_P1}, {"rule", IKE_SVC_GET_RULE}, {"preshared", IKE_SVC_GET_PS}, {"defaults", IKE_SVC_GET_DEFS}, {NULL, IKE_SVC_ERROR} } }, {"set", IKE_SVC_ERROR, { {"debug", IKE_SVC_SET_DBG}, {"priv", IKE_SVC_SET_PRIV}, {NULL, IKE_SVC_ERROR} } }, {"token", IKE_SVC_ERROR, { {"login", IKE_SVC_SET_PIN}, {"logout", IKE_SVC_DEL_PIN}, {NULL, IKE_SVC_ERROR}, } }, {"add", IKE_SVC_ERROR, { {"rule", IKE_SVC_NEW_RULE}, {"preshared", IKE_SVC_NEW_PS}, {NULL, IKE_SVC_ERROR} } }, {"del", IKE_SVC_ERROR, { {"p1", IKE_SVC_DEL_P1}, {"rule", IKE_SVC_DEL_RULE}, {"preshared", IKE_SVC_DEL_PS}, {NULL, IKE_SVC_ERROR} } }, {"dump", IKE_SVC_ERROR, { {"p1", IKE_SVC_DUMP_P1S}, {"rule", IKE_SVC_DUMP_RULES}, {"preshared", IKE_SVC_DUMP_PS}, {"certcache", IKE_SVC_DUMP_CERTCACHE}, {"groups", IKE_SVC_DUMP_GROUPS}, {"encralgs", IKE_SVC_DUMP_ENCRALGS}, {"authalgs", IKE_SVC_DUMP_AUTHALGS}, {NULL, IKE_SVC_ERROR} } }, {"flush", IKE_SVC_ERROR, { {"p1", IKE_SVC_FLUSH_P1S}, {"certcache", IKE_SVC_FLUSH_CERTCACHE}, {NULL, IKE_SVC_ERROR} } }, {"read", IKE_SVC_ERROR, { {"rule", IKE_SVC_READ_RULES}, {"preshared", IKE_SVC_READ_PS}, {NULL, IKE_SVC_ERROR} } }, {"write", IKE_SVC_ERROR, { {"rule", IKE_SVC_WRITE_RULES}, {"preshared", IKE_SVC_WRITE_PS}, {NULL, IKE_SVC_ERROR} } }, {"help", IKEADM_HELP_GENERAL, { {"get", IKEADM_HELP_GET}, {"set", IKEADM_HELP_SET}, {"add", IKEADM_HELP_ADD}, {"del", IKEADM_HELP_DEL}, {"dump", IKEADM_HELP_DUMP}, {"flush", IKEADM_HELP_FLUSH}, {"read", IKEADM_HELP_READ}, {"write", IKEADM_HELP_WRITE}, {"token", IKEADM_HELP_TOKEN}, {"help", IKEADM_HELP_HELP}, {NULL, IKE_SVC_ERROR} } }, {"exit", IKEADM_EXIT, { {NULL, IKE_SVC_ERROR} } }, {"quit", IKEADM_EXIT, { {NULL, IKE_SVC_ERROR} } }, {"dbg", IKE_SVC_ERROR, { {"rbdump", IKE_SVC_DBG_RBDUMP}, {NULL, IKE_SVC_ERROR} } }, {NULL, IKE_SVC_ERROR, { {NULL, IKE_SVC_ERROR} } } }; struct cmdtbl *ct = table; struct objtbl *ot; if (cmdstr == NULL) { return (IKE_SVC_ERROR); } while (ct->cmd != NULL && strcmp(ct->cmd, cmdstr) != 0) ct++; ot = ct->objt; if (ct->cmd == NULL) { message(gettext("Unrecognized command '%s'"), cmdstr); return (ot->token); } if (objstr == NULL) { return (ct->null_obj_token); } while (ot->obj != NULL && strcmp(ot->obj, objstr) != 0) ot++; if (ot->obj == NULL) message(gettext("Unrecognized object '%s'"), objstr); return (ot->token); } /* * Parsing functions: * Parse command-line identification info. All return -1 on failure, * or the number of cmd-line args "consumed" on success (though argc * and argv params are not actually modified). */ static int parse_label(int argc, char **argv, char *label) { if ((argc < 1) || (argv == NULL)) return (-1); if (strlcpy(label, argv[0], MAX_LABEL_LEN) >= MAX_LABEL_LEN) return (-1); return (1); } /* * Parse a PKCS#11 token get the label. */ static int parse_token(int argc, char **argv, char *token_label) { if ((argc < 1) || (argv == NULL)) return (-1); if (strlcpy(token_label, argv[0], PKCS11_TOKSIZE) >= PKCS11_TOKSIZE) return (-1); return (0); } /* * Parse an address off the command line. In the hpp param, either * return a hostent pointer (caller frees) or a pointer to a dummy_he_t * (must also be freed by the caller; both cases are handled by the * macro FREE_HE). The new getipnodebyname() call does the Right Thing * (TM), even with raw addresses (colon-separated IPv6 or dotted decimal * IPv4). * (mostly stolen from ipseckey.c, though some tweaks were made * to better serve our purposes here.) */ typedef struct { struct hostent he; char *addtl[2]; } dummy_he_t; static int parse_addr(int argc, char **argv, struct hostent **hpp) { int hp_errno; struct hostent *hp = NULL; dummy_he_t *dhp; char *addr1; if ((argc < 1) || (argv == NULL) || (argv[0] == NULL)) return (-1); if (!nflag) { /* * Try name->address first. Assume AF_INET6, and * get IPV4s, plus IPv6s iff IPv6 is configured. */ hp = getipnodebyname(argv[0], AF_INET6, AI_DEFAULT | AI_ALL, &hp_errno); } else { /* * Try a normal address conversion only. malloc a * dummy_he_t to construct a fake hostent. Caller * will know to free this one using free_he(). */ dhp = (dummy_he_t *)malloc(sizeof (dummy_he_t)); addr1 = (char *)malloc(sizeof (struct in6_addr)); if (inet_pton(AF_INET6, argv[0], addr1) == 1) { dhp->he.h_addr_list = dhp->addtl; dhp->addtl[0] = addr1; dhp->addtl[1] = NULL; hp = &dhp->he; dhp->he.h_addrtype = AF_INET6; dhp->he.h_length = sizeof (struct in6_addr); } else if (inet_pton(AF_INET, argv[0], addr1) == 1) { dhp->he.h_addr_list = dhp->addtl; dhp->addtl[0] = addr1; dhp->addtl[1] = NULL; hp = &dhp->he; dhp->he.h_addrtype = AF_INET; dhp->he.h_length = sizeof (struct in_addr); } else { hp = NULL; } } *hpp = hp; if (hp == NULL) { message(gettext("Unknown address %s."), argv[0]); return (-1); } return (1); } /* * Free a dummy_he_t structure that was malloc'd in parse_addr(). * Unfortunately, callers of parse_addr don't want to know about * dummy_he_t structs, so all they have is a pointer to the struct * hostent; so that's what's passed in. To manage this, we make * the assumption that the struct hostent is the first field in * the dummy_he_t, and therefore a pointer to it is a pointer to * the dummy_he_t. */ static void free_he(struct hostent *hep) { dummy_he_t *p = (dummy_he_t *)hep; assert(p != NULL); if (p->addtl[0]) free(p->addtl[0]); if (p->addtl[1]) free(p->addtl[1]); free(p); } #define FREE_HE(x) \ if (nflag) \ free_he(x); \ else \ freehostent(x) static void headdr2sa(char *hea, struct sockaddr_storage *sa, int len) { struct sockaddr_in *sin; struct sockaddr_in6 *sin6; if (len == sizeof (struct in6_addr)) { /* LINTED E_BAD_PTR_CAST_ALIGN */ if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)hea)) { sin = (struct sockaddr_in *)sa; (void) memset(sin, 0, sizeof (*sin)); /* LINTED E_BAD_PTR_CAST_ALIGN */ IN6_V4MAPPED_TO_INADDR((struct in6_addr *)hea, &sin->sin_addr); sin->sin_family = AF_INET; } else { sin6 = (struct sockaddr_in6 *)sa; (void) memset(sin6, 0, sizeof (*sin6)); (void) memcpy(&sin6->sin6_addr, hea, sizeof (struct in6_addr)); sin6->sin6_family = AF_INET6; } } else { sin = (struct sockaddr_in *)sa; (void) memset(sin, 0, sizeof (*sin)); (void) memcpy(&sin->sin_addr, hea, sizeof (struct in_addr)); sin->sin_family = AF_INET; } } /* * The possible ident-type keywords that might be used on the command * line. This is a superset of the ones supported by ipseckey, those * in the ike config file, and those in ike.preshared. */ static keywdtab_t idtypes[] = { /* ip, ipv4, and ipv6 are valid for preshared keys... */ {SADB_IDENTTYPE_RESERVED, "ip"}, {SADB_IDENTTYPE_RESERVED, "ipv4"}, {SADB_IDENTTYPE_RESERVED, "ipv6"}, {SADB_IDENTTYPE_PREFIX, "prefix"}, {SADB_IDENTTYPE_PREFIX, "ipv4-prefix"}, {SADB_IDENTTYPE_PREFIX, "ipv6-prefix"}, {SADB_IDENTTYPE_PREFIX, "subnet"}, {SADB_IDENTTYPE_PREFIX, "subnetv4"}, {SADB_IDENTTYPE_PREFIX, "subnetv6"}, {SADB_IDENTTYPE_FQDN, "fqdn"}, {SADB_IDENTTYPE_FQDN, "dns"}, {SADB_IDENTTYPE_FQDN, "domain"}, {SADB_IDENTTYPE_FQDN, "domainname"}, {SADB_IDENTTYPE_USER_FQDN, "user_fqdn"}, {SADB_IDENTTYPE_USER_FQDN, "mbox"}, {SADB_IDENTTYPE_USER_FQDN, "mailbox"}, {SADB_X_IDENTTYPE_DN, "dn"}, {SADB_X_IDENTTYPE_DN, "asn1dn"}, {SADB_X_IDENTTYPE_GN, "gn"}, {SADB_X_IDENTTYPE_GN, "asn1gn"}, {SADB_X_IDENTTYPE_ADDR_RANGE, "ipv4-range"}, {SADB_X_IDENTTYPE_ADDR_RANGE, "ipv6-range"}, {SADB_X_IDENTTYPE_ADDR_RANGE, "rangev4"}, {SADB_X_IDENTTYPE_ADDR_RANGE, "rangev6"}, {SADB_X_IDENTTYPE_KEY_ID, "keyid"}, {0, NULL} }; static int parse_idtype(char *type, uint16_t *idnum) { keywdtab_t *idp; if (type == NULL) return (-1); for (idp = idtypes; idp->kw_str != NULL; idp++) { if (strcasecmp(idp->kw_str, type) == 0) { if (idnum != NULL) *idnum = idp->kw_tag; return (1); } } return (-1); } /* * The sadb_ident_t is malloc'd, since its length varies; * so the caller must free() it when done with the data. */ static int parse_ident(int argc, char **argv, sadb_ident_t **idpp) { int alloclen, consumed; sadb_ident_t *idp; if ((argc < 2) || (argv == NULL) || (argv[0] == NULL) || (argv[1] == NULL)) return (-1); alloclen = sizeof (sadb_ident_t) + IKEDOORROUNDUP(strlen(argv[1]) + 1); *idpp = idp = (sadb_ident_t *)malloc(alloclen); if (idp == NULL) Bail("parsing identity"); if ((consumed = parse_idtype(argv[0], &idp->sadb_ident_type)) < 0) { message(gettext("unknown identity type %s."), argv[0]); return (-1); } idp->sadb_ident_len = SADB_8TO64(alloclen); idp->sadb_ident_reserved = 0; idp->sadb_ident_id = 0; /* now copy in identity param */ (void) strlcpy((char *)(idp + 1), argv[1], alloclen - (sizeof (sadb_ident_t))); return (++consumed); } static int parse_cky(int argc, char **argv, uint64_t *ckyp) { u_longlong_t arg; if ((argc < 1) || (argv[0] == NULL)) return (-1); errno = 0; arg = strtoull(argv[0], NULL, 0); if (errno != 0) { message(gettext("failed to parse cookie %s."), argv[0]); return (-1); } *ckyp = (uint64_t)arg; return (1); } static int parse_addr_pr(int argc, char **argv, struct hostent **h1pp, struct hostent **h2pp) { int rtn, consumed = 0; if ((rtn = parse_addr(argc, argv, h1pp)) < 0) { return (-1); } consumed = rtn; argc -= rtn; argv += rtn; if ((rtn = parse_addr(argc, argv, h2pp)) < 0) { FREE_HE(*h1pp); return (-1); } consumed += rtn; return (consumed); } /* * The sadb_ident_ts are malloc'd, since their length varies; * so the caller must free() them when done with the data. */ static int parse_ident_pr(int argc, char **argv, sadb_ident_t **id1pp, sadb_ident_t **id2pp) { int rtn, consumed = 0; if ((rtn = parse_ident(argc, argv, id1pp)) < 0) { return (-1); } consumed = rtn; argc -= rtn; argv += rtn; (*id1pp)->sadb_ident_exttype = SADB_EXT_IDENTITY_SRC; if ((rtn = parse_ident(argc, argv, id2pp)) < 0) { free(*id1pp); return (-1); } consumed += rtn; (*id2pp)->sadb_ident_exttype = SADB_EXT_IDENTITY_DST; return (consumed); } static int parse_cky_pr(int argc, char **argv, ike_cky_pr_t *cpr) { int rtn, consumed = 0; if ((rtn = parse_cky(argc, argv, &cpr->cky_i)) < 0) { return (-1); } consumed = rtn; argc -= rtn; argv += rtn; if ((rtn = parse_cky(argc, argv, &cpr->cky_r)) < 0) { return (-1); } consumed += rtn; return (consumed); } /* * Preshared key field types...used for parsing preshared keys that * have been entered on the command line. The code to parse preshared * keys (parse_ps, parse_key, parse_psfldid, parse_ikmtype, ...) is * mostly duplicated from in.iked's readps.c. */ #define PSFLD_LOCID 1 #define PSFLD_LOCIDTYPE 2 #define PSFLD_REMID 3 #define PSFLD_REMIDTYPE 4 #define PSFLD_MODE 5 #define PSFLD_KEY 6 static keywdtab_t psfldtypes[] = { {PSFLD_LOCID, "localid"}, {PSFLD_LOCIDTYPE, "localidtype"}, {PSFLD_REMID, "remoteid"}, {PSFLD_REMIDTYPE, "remoteidtype"}, {PSFLD_MODE, "ike_mode"}, {PSFLD_KEY, "key"}, {0, NULL} }; static int parse_psfldid(char *type, uint16_t *idnum) { keywdtab_t *pfp; if (type == NULL) return (-1); for (pfp = psfldtypes; pfp->kw_str != NULL; pfp++) { if (strcasecmp(pfp->kw_str, type) == 0) { if (idnum != NULL) *idnum = pfp->kw_tag; return (1); } } return (-1); } static keywdtab_t ikemodes[] = { {IKE_XCHG_IDENTITY_PROTECT, "main"}, {IKE_XCHG_AGGRESSIVE, "aggressive"}, {IKE_XCHG_IP_AND_AGGR, "both"}, {0, NULL} }; static int parse_ikmtype(char *mode, uint16_t *modenum) { keywdtab_t *ikmp; if (mode == NULL) return (-1); for (ikmp = ikemodes; ikmp->kw_str != NULL; ikmp++) { if (strcasecmp(ikmp->kw_str, mode) == 0) { if (modenum != NULL) *modenum = ikmp->kw_tag; return (1); } } return (-1); } #define hd2num(hd) (((hd) >= '0' && (hd) <= '9') ? ((hd) - '0') : \ (((hd) >= 'a' && (hd) <= 'f') ? ((hd) - 'a' + 10) : ((hd) - 'A' + 10))) static uint8_t * parse_key(char *input, uint_t *keybuflen, uint_t *lbits) { uint8_t *keyp, *keybufp; uint_t i, hexlen = 0, bits, alloclen; for (i = 0; input[i] != '\0' && input[i] != '/'; i++) hexlen++; if (input[i] == '\0') { bits = 0; } else { /* Have /nn. */ input[i] = '\0'; if (sscanf((input + i + 1), "%u", &bits) != 1) return (NULL); /* hexlen is in nibbles */ if (((bits + 3) >> 2) > hexlen) return (NULL); /* * Adjust hexlen down if user gave us too small of a bit * count. */ if ((hexlen << 2) > bits + 3) { hexlen = (bits + 3) >> 2; input[hexlen] = '\0'; } } /* * Allocate. Remember, hexlen is in nibbles. */ alloclen = (hexlen/2 + (hexlen & 0x1)); keyp = malloc(alloclen); if (keyp == NULL) return (NULL); keybufp = keyp; *keybuflen = alloclen; if (bits == 0) *lbits = (hexlen + (hexlen & 0x1)) << 2; else *lbits = bits; /* * Read in nibbles. Read in odd-numbered as shifted high. * (e.g. 123 becomes 0x1230). */ for (i = 0; input[i] != '\0'; i += 2) { boolean_t second = (input[i + 1] != '\0'); if (!isxdigit(input[i]) || (!isxdigit(input[i + 1]) && second)) { free(keyp); return (NULL); } *keyp = (hd2num(input[i]) << 4); if (second) *keyp |= hd2num(input[i + 1]); else break; /* out of for loop. */ keyp++; } /* zero the remaining bits if we're a non-octet amount. */ if (bits & 0x7) *((input[i] == '\0') ? keyp - 1 : keyp) &= 0xff << (8 - (bits & 0x7)); return (keybufp); } /* * the ike_ps_t struct (plus trailing data) will be allocated here, * so it will need to be freed by the caller. */ static int parse_ps(int argc, char **argv, ike_ps_t **presharedpp, int *len) { uint_t c = 0, locidlen, remidlen, keylen, keybits; uint_t a_locidtotal = 0, a_remidtotal = 0; char *locid, *remid, *locpfx = NULL, *rempfx = NULL; uint8_t *keyp = NULL; uint16_t fldid, locidtype, remidtype, mtype; struct hostent *loche = NULL, *remhe = NULL; ike_ps_t *psp = NULL; sadb_ident_t *sidp; boolean_t whacked = B_FALSE; int pfxlen = 0; if ((argv[c] == NULL) || (argv[c][0] != '{')) return (-1); if (argv[c][1] != 0) { /* no space between '{' and first token */ argv[c]++; } else { c++; } if ((argv[argc - 1][strlen(argv[argc - 1]) - 1] == '}') && (argv[argc - 1][0] != '}')) { /* * whack '}' without a space before it or parsers break. * Remember this trailing character for later */ argv[argc - 1][strlen(argv[argc - 1]) - 1] = '\0'; whacked = B_TRUE; } /* Default to type IP */ locidtype = remidtype = SADB_IDENTTYPE_RESERVED; /* Default to base exchanges */ mtype = IKE_XCHG_BASE; while ((c < argc) && (argv[c] != NULL) && (argv[c][0] != '}')) { if ((argv[c + 1] == NULL) || (argv[c + 1][0] == '}')) goto bail; if (parse_psfldid(argv[c++], &fldid) < 0) goto bail; switch (fldid) { case PSFLD_LOCID: locid = argv[c++]; locidlen = strlen(locid) + 1; break; case PSFLD_LOCIDTYPE: if (parse_idtype(argv[c++], &locidtype) < 0) goto bail; break; case PSFLD_REMID: remid = argv[c++]; remidlen = strlen(remid) + 1; break; case PSFLD_REMIDTYPE: if (parse_idtype(argv[c++], &remidtype) < 0) goto bail; break; case PSFLD_MODE: if (parse_ikmtype(argv[c++], &mtype) < 0) goto bail; break; case PSFLD_KEY: keyp = parse_key(argv[c++], &keylen, &keybits); if (keyp == NULL) goto bail; break; } } /* Make sure the line was terminated with '}' */ if (argv[c] == NULL) { if (!whacked) goto bail; } else if (argv[c][0] != '}') { goto bail; } /* * make sure we got all the required fields. If no idtype, assume * ip addr; if that translation fails, we'll catch the error then. */ if (locid == NULL || remid == NULL || keyp == NULL || mtype == 0) goto bail; /* figure out the size buffer we need */ *len = sizeof (ike_ps_t); if (locidtype != SADB_IDENTTYPE_RESERVED) { a_locidtotal = IKEDOORROUNDUP(sizeof (sadb_ident_t) + locidlen); *len += a_locidtotal; } if (remidtype != SADB_IDENTTYPE_RESERVED) { a_remidtotal = IKEDOORROUNDUP(sizeof (sadb_ident_t) + remidlen); *len += a_remidtotal; } *len += keylen; psp = malloc(*len); if (psp == NULL) goto bail; (void) memset(psp, 0, *len); psp->ps_ike_mode = mtype; psp->ps_localid_off = sizeof (ike_ps_t); if (locidtype == SADB_IDENTTYPE_RESERVED) { locpfx = strchr(locid, '/'); if (locpfx != NULL) { *locpfx = '\0'; locpfx++; } /* * this is an ip address, store in the sockaddr field; * we won't use an sadb_ident_t. */ psp->ps_localid_len = 0; if (parse_addr(1, &locid, &loche) < 0) goto bail; if (loche->h_addr_list[1] != NULL) { message(gettext("preshared key identifier cannot " "match multiple IP addresses")); goto bail; } headdr2sa(loche->h_addr_list[0], &psp->ps_ipaddrs.loc_addr, loche->h_length); FREE_HE(loche); } else { psp->ps_localid_len = sizeof (sadb_ident_t) + locidlen; sidp = (sadb_ident_t *)((int)psp + psp->ps_localid_off); sidp->sadb_ident_len = psp->ps_localid_len; sidp->sadb_ident_type = locidtype; (void) strlcpy((char *)(sidp + 1), locid, a_locidtotal); } psp->ps_remoteid_off = psp->ps_localid_off + a_locidtotal; if (remidtype == SADB_IDENTTYPE_RESERVED) { rempfx = strchr(remid, '/'); if (rempfx != NULL) { *rempfx = '\0'; rempfx++; } /* * this is an ip address, store in the sockaddr field; * we won't use an sadb_ident_t. */ psp->ps_remoteid_len = 0; if (parse_addr(1, &remid, &remhe) < 0) goto bail; if (remhe->h_addr_list[1] != NULL) { message(gettext("preshared key identifier cannot " "match multiple IP addresses")); goto bail; } headdr2sa(remhe->h_addr_list[0], &psp->ps_ipaddrs.rem_addr, remhe->h_length); FREE_HE(remhe); } else { /* make sure we have at least 16-bit alignment */ if (remidlen & 0x1) remidlen++; psp->ps_remoteid_len = sizeof (sadb_ident_t) + remidlen; sidp = (sadb_ident_t *)((int)psp + psp->ps_remoteid_off); sidp->sadb_ident_len = psp->ps_remoteid_len; sidp->sadb_ident_type = remidtype; (void) strlcpy((char *)(sidp + 1), remid, a_remidtotal); } psp->ps_key_off = psp->ps_remoteid_off + a_remidtotal; psp->ps_key_len = keylen; psp->ps_key_bits = keybits; (void) memcpy((uint8_t *)((int)psp + psp->ps_key_off), keyp, keylen); if (locpfx != NULL && ((pfxlen = atoi(locpfx)) > 0)) psp->ps_localid_plen = pfxlen; if (rempfx != NULL && ((pfxlen = atoi(rempfx)) > 0)) psp->ps_remoteid_plen = pfxlen; *presharedpp = psp; return (c); bail: if (loche != NULL) FREE_HE(loche); if (remhe != NULL) FREE_HE(remhe); if (keyp != NULL) free(keyp); if (psp != NULL) free(psp); *presharedpp = NULL; return (-1); } /* * Printing functions * * A potential point of confusion here is that the ikeadm-specific string- * producing functions do not match the ipsec_util.c versions in style: the * ikeadm-specific functions return a string (and are named foostr), while * the ipsec_util.c functions actually print the string to the file named * in the second arg to the function (and are named dump_foo). * * Localization for ikeadm seems more straightforward when complete * phrases are translated rather than: a part of a phrase, a call to * dump_foo(), and more of the phrase. It could also accommodate * non-English grammar more easily. */ static char * errstr(int err) { static char rtn[MAXLINESIZE]; switch (err) { case IKE_ERR_NO_OBJ: return (gettext("No data returned")); case IKE_ERR_NO_DESC: return (gettext("No destination provided")); case IKE_ERR_ID_INVALID: return (gettext("Id info invalid")); case IKE_ERR_LOC_INVALID: return (gettext("Destination invalid")); case IKE_ERR_CMD_INVALID: return (gettext("Command invalid")); case IKE_ERR_DATA_INVALID: return (gettext("Supplied data invalid")); case IKE_ERR_CMD_NOTSUP: return (gettext("Unknown command")); case IKE_ERR_REQ_INVALID: return (gettext("Request invalid")); case IKE_ERR_NO_PRIV: return (gettext("Not allowed at current privilege level")); case IKE_ERR_NO_AUTH: return (gettext("User not authorized")); case IKE_ERR_SYS_ERR: return (gettext("System error")); case IKE_ERR_DUP_IGNORED: return (gettext("One or more duplicate entries ignored")); case IKE_ERR_NO_TOKEN: return (gettext( "token login failed or no objects on device")); case IKE_ERR_IN_PROGRESS: return (gettext( "Duplicate operation already in progress")); case IKE_ERR_NO_MEM: return (gettext( "Insufficient memory")); default: (void) snprintf(rtn, MAXLINESIZE, gettext(""), err); return (rtn); } } static char * dbgstr(int bit) { static char rtn[MAXLINESIZE]; switch (bit) { case D_CERT: return (gettext("Certificate management")); case D_KEY: return (gettext("Key management")); case D_OP: return (gettext("Operational")); case D_P1: return (gettext("Phase 1 SA creation")); case D_P2: return (gettext("Phase 2 SA creation")); case D_PFKEY: return (gettext("PF_KEY interface")); case D_POL: return (gettext("Policy management")); case D_PROP: return (gettext("Proposal construction")); case D_DOOR: return (gettext("Door interface")); case D_CONFIG: return (gettext("Config file processing")); case D_LABEL: return (gettext("MAC label processing")); default: (void) snprintf(rtn, MAXLINESIZE, gettext(""), bit); return (rtn); } } static char * privstr(int priv) { static char rtn[MAXLINESIZE]; switch (priv) { case IKE_PRIV_MINIMUM: return (gettext("base privileges")); case IKE_PRIV_MODKEYS: return (gettext("access to preshared key information")); case IKE_PRIV_KEYMAT: return (gettext("access to keying material")); default: (void) snprintf(rtn, MAXLINESIZE, gettext(""), priv); return (rtn); } } static char * xchgstr(int xchg) { static char rtn[MAXLINESIZE]; switch (xchg) { case IKE_XCHG_NONE: return (gettext("")); case IKE_XCHG_BASE: return (gettext("base")); case IKE_XCHG_IDENTITY_PROTECT: return (gettext("main mode (identity protect)")); case IKE_XCHG_AUTH_ONLY: return (gettext("authentication only")); case IKE_XCHG_AGGRESSIVE: return (gettext("aggressive mode")); case IKE_XCHG_IP_AND_AGGR: return (gettext("main and aggressive mode")); case IKE_XCHG_ANY: return (gettext("any mode")); default: (void) snprintf(rtn, MAXLINESIZE, gettext(""), xchg); return (rtn); } } static char * statestr(int state) { static char rtn[MAXLINESIZE]; switch (state) { case IKE_SA_STATE_INIT: return (gettext("INITIALIZING")); case IKE_SA_STATE_SENT_SA: return (gettext("SENT FIRST MSG (SA)")); case IKE_SA_STATE_SENT_KE: return (gettext("SENT SECOND MSG (KE)")); case IKE_SA_STATE_SENT_LAST: return (gettext("SENT FINAL MSG")); case IKE_SA_STATE_DONE: return (gettext("ACTIVE")); case IKE_SA_STATE_DELETED: return (gettext("DELETED")); case IKE_SA_STATE_INVALID: return (gettext("")); default: (void) snprintf(rtn, MAXLINESIZE, gettext(""), state); return (rtn); } } static char * authmethstr(int meth) { static char rtn[MAXLINESIZE]; switch (meth) { case IKE_AUTH_METH_PRE_SHARED_KEY: return (gettext("pre-shared key")); case IKE_AUTH_METH_DSS_SIG: return (gettext("DSS signatures")); case IKE_AUTH_METH_RSA_SIG: return (gettext("RSA signatures")); case IKE_AUTH_METH_RSA_ENCR: return (gettext("RSA Encryption")); case IKE_AUTH_METH_RSA_ENCR_REVISED: return (gettext("Revised RSA Encryption")); default: (void) snprintf(rtn, MAXLINESIZE, gettext(""), meth); return (rtn); } } static char * prfstr(int prf) { static char rtn[MAXLINESIZE]; switch (prf) { case IKE_PRF_NONE: return (gettext("")); case IKE_PRF_HMAC_MD5: return ("HMAC MD5"); case IKE_PRF_HMAC_SHA1: return ("HMAC SHA1"); case IKE_PRF_HMAC_SHA256: return ("HMAC SHA256"); case IKE_PRF_HMAC_SHA384: return ("HMAC SHA384"); case IKE_PRF_HMAC_SHA512: return ("HMAC SHA512"); default: (void) snprintf(rtn, MAXLINESIZE, gettext(""), prf); return (rtn); } } static char * dhstr(int grp) { static char rtn[MAXLINESIZE]; switch (grp) { case 0: return (gettext("")); case IKE_GRP_DESC_MODP_768: return (gettext("768-bit MODP (group 1)")); case IKE_GRP_DESC_MODP_1024: return (gettext("1024-bit MODP (group 2)")); case IKE_GRP_DESC_EC2N_155: return (gettext("EC2N group on GP[2^155]")); case IKE_GRP_DESC_EC2N_185: return (gettext("EC2N group on GP[2^185]")); case IKE_GRP_DESC_MODP_1536: return (gettext("1536-bit MODP (group 5)")); case IKE_GRP_DESC_MODP_2048: return (gettext("2048-bit MODP (group 14)")); case IKE_GRP_DESC_MODP_3072: return (gettext("3072-bit MODP (group 15)")); case IKE_GRP_DESC_MODP_4096: return (gettext("4096-bit MODP (group 16)")); case IKE_GRP_DESC_MODP_6144: return (gettext("6144-bit MODP (group 17)")); case IKE_GRP_DESC_MODP_8192: return (gettext("8192-bit MODP (group 18)")); case IKE_GRP_DESC_ECP_256: return (gettext("256-bit ECP (group 19)")); case IKE_GRP_DESC_ECP_384: return (gettext("384-bit ECP (group 20)")); case IKE_GRP_DESC_ECP_521: return (gettext("521-bit ECP (group 21)")); case IKE_GRP_DESC_MODP_1024_160: return ( gettext("1024-bit MODP with 160-bit subprime (group 22)")); case IKE_GRP_DESC_MODP_2048_224: return ( gettext("2048-bit MODP with 224-bit subprime (group 23)")); case IKE_GRP_DESC_MODP_2048_256: return ( gettext("2048-bit MODP with 256-bit subprime (group 24)")); case IKE_GRP_DESC_ECP_192: return (gettext("192-bit ECP (group 25)")); case IKE_GRP_DESC_ECP_224: return (gettext("224-bit ECP (group 26)")); default: (void) snprintf(rtn, MAXLINESIZE, gettext(""), grp); return (rtn); } } static void print_hdr(char *prefix, ike_p1_hdr_t *hdrp) { char sbuf[TBUF_SIZE]; char tbuf[TBUF_SIZE]; time_t ltime = (time_t)hdrp->p1hdr_dpd_time; (void) printf( gettext("%s Cookies: Initiator 0x%llx Responder 0x%llx\n"), prefix, ntohll(hdrp->p1hdr_cookies.cky_i), ntohll(hdrp->p1hdr_cookies.cky_r)); (void) printf(gettext("%s The local host is the %s.\n"), prefix, hdrp->p1hdr_isinit ? gettext("initiator") : gettext("responder")); (void) printf(gettext("%s ISAKMP version %d.%d; %s exchange\n"), prefix, hdrp->p1hdr_major, hdrp->p1hdr_minor, xchgstr(hdrp->p1hdr_xchg)); (void) printf(gettext("%s Current state is %s\n"), prefix, statestr(hdrp->p1hdr_state)); if (hdrp->p1hdr_support_dpd == B_FALSE) { return; } (void) printf(gettext("%s Dead Peer Detection (RFC 3706)" " enabled"), prefix); if (hdrp->p1hdr_dpd_state < DPD_IN_PROGRESS) { (void) printf("\n"); return; } if (strftime(tbuf, TBUF_SIZE, NULL, localtime(<ime)) == 0) { (void) strlcpy(tbuf, gettext("