# # 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 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # include ../Makefile.lib HDRS = ipsec_util.h ikedoor.h errfp.h HDRDIR = common # Hammerhead: amd64-only SUBDIRS = $(MACH64) all : TARGET = all clean : TARGET = clean clobber : TARGET = clobber install : TARGET = install # # Definitions for message catalog # MSGFILES = `$(GREP) -l gettext common/*.[ch]` POFILE = libipsecutil.po .KEEP_STATE: all clean clobber install: $(SUBDIRS) $(POFILE): pofile_MSGFILES install_h: $(ROOTHDRS) check: $(CHECKHDRS) _msg: $(MSGDOMAINPOFILE) include ../../Makefile.msg.targ $(SUBDIRS): FRC @cd $@; pwd; $(MAKE) $(TARGET) FRC: include ../Makefile.targ # # 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 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # Copyright 2018, Joyent, Inc. # LIBRARY = libipsecutil.a VERS = .1 OBJECTS = ipsec_util.o algs.o include ../../Makefile.lib LIBS += $(DYNLIB) SRCDIR = ../common BERDIR = $(SRC)/lib/libkmf/ber_der/inc LDLIBS += -ltecla -lsocket -lnsl -lc -lkmf -lkmfberder LAZYLIBS = $(ZLAZYLOAD) -ltsol $(ZNOLAZYLOAD) LDLIBS += $(LAZYLIBS) CFLAGS += $(CCVERBOSE) CPPFLAGS += -I$(SRCDIR) -I$(BERDIR) CERRWARN += -Wno-unused-function CERRWARN += $(CNOWARN_UNINIT) SMOFF += shift_to_zero .KEEP_STATE: all: $(LIBS) include ../../Makefile.targ # # 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 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # include ../Makefile.com include ../../Makefile.lib.64 install: all $(ROOTLIBS64) $(ROOTLINKS64) /* * 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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include static char *preamble = "# /etc/inet/ipsecalgs output from ipsecalgs(8)\n" "#\n" "# DO NOT EDIT OR PARSE THIS FILE!\n" "#\n" "# Use the ipsecalgs(8) command to change the contents of this file.\n" "# The algorithm descriptions contained in this file are synchronised to the\n" "# kernel with ipsecalgs -s, the kernel validates the entries at this point." "\n\n" "# PROTO|protocol-id|protocol-name|exec-mode\n" "## NOTE: Some protocol numbers are well-known and defined in \n\n" "# ALG|protocol-id|alg-id|name,name,...|ef-id| \n" "# {default/}{key,key..}or{key-key,inc}|block_size or MAC-size|\n" "# [parameter,parameter..]|[flags]\n\n" "#\n" "## Note: Parameters and flags only apply to certain algorithms.\n\n"; #define CFG_PERMS S_IRUSR | S_IRGRP | S_IROTH /* Perms 0444. */ #define CFG_OWNER 0 /* root */ #define CFG_GROUP 1 /* "other" */ /* * write_new_algfile() helper macros to check for write errors. */ #define FPRINTF_ERR(fcall) if ((fcall) < 0) { \ rc = LIBIPSEC_ALGS_DIAG_ALGSFILEWRITE; \ goto bail; \ } #define FPUT_ERR(fcall) if ((fcall) == EOF) { \ rc = LIBIPSEC_ALGS_DIAG_ALGSFILEWRITE; \ goto bail; \ } /* * Helper macros to start and finish a list of entries that were added * as part of a package installation. */ #define PKG_SEC_START(pkgname, doing_pkg, cur_pkg) { \ (void) strcpy((cur_pkg), (pkgname)); \ FPRINTF_ERR(fprintf(f, "%s%s\n", \ LIBIPSEC_ALGS_LINE_PKGSTART, (cur_pkg))); \ (doing_pkg) = B_TRUE; \ } #define PKG_SEC_END(doing_pkg, cur_pkg) { \ if (doing_pkg) { \ FPRINTF_ERR(fprintf(f, "%s%s\n", \ LIBIPSEC_ALGS_LINE_PKGEND, (cur_pkg))); \ (doing_pkg) = B_FALSE; \ } \ } /* * Take a zero-terminated int array and print int1,int2...,intN. * If zero-only, then print a single '0'. * Returns 0 on success, -1 if an error occurred while writing to * the specified file. */ int list_ints(FILE *f, int *floater) { boolean_t executed = B_FALSE; while (*floater != 0) { executed = B_TRUE; if (fprintf(f, "%d", *floater) < 0) return (-1); if (*(++floater) != 0) if (fputc(',', f) == EOF) return (-1); } if (!executed) if (fputc('0', f) == EOF) return (-1); return (0); } /* * If the specified algorithm was defined within a package section, i.e. * between the lines "# Start " and "# End ", returns * the value of . */ static char * alg_has_pkg(ipsec_proto_t *proto, struct ipsecalgent *alg) { int i; if (proto->proto_algs_pkgs == NULL) return (NULL); for (i = 0; i < proto->proto_algs_npkgs; i++) if (proto->proto_algs_pkgs[i].alg_num == alg->a_alg_num) return (proto->proto_algs_pkgs[i].pkg_name); return (NULL); } /* * Writes the package start/end delimiters according to the package * name associated with the current protocol or algorithm, and * the state of the packaging information already written to the file. * Called by write_new_algfile(). Returns 0 on success, one of the * LIBIPSEC_DIAG codes on failure. */ static int pkg_section(FILE *f, char *pkg_name, boolean_t *doing_pkg, char *cur_pkg) { int rc = 0; if (pkg_name != NULL) { /* protocol or algorithm is associated with a package */ if (!*doing_pkg) { /* start of a new package section */ PKG_SEC_START(pkg_name, *doing_pkg, cur_pkg); } else { /* already in a package section */ if (strcmp(pkg_name, cur_pkg) != 0) { /* different package name */ PKG_SEC_END(*doing_pkg, cur_pkg); PKG_SEC_START(pkg_name, *doing_pkg, cur_pkg); } } } else if (*doing_pkg) { /* in a package section when the entry isn't */ PKG_SEC_END(*doing_pkg, cur_pkg); } bail: return (rc); } /* * Given a list of protocols and number, write them to a new algorithm file. * This function takes num_protos + num_protos * dois-per-alg operations. * Also free the protocol structure. * * Note that no locking spans the read/update/write phases that can be * used by callers of this routine. This could cause this function to suffer * from the "lost update" problem. Since updates to the IPsec protocols * and algorithm tables are very infrequent, this should not be a issue in * practice. */ static int write_new_algfile(ipsec_proto_t *protos, int num_protos) { FILE *f; int fd, i, j, k; int rc = 0; struct ipsecalgent *alg; char cur_pkg[1024]; boolean_t doing_pkg = B_FALSE; char *alg_pkg; char tmp_name_template[] = INET_IPSECALGSPATH "ipsecalgsXXXXXX"; char *tmp_name; /* * In order to avoid potentially corrupting the configuration * file on file system failure, write the new configuration info * to a temporary file which is then renamed to the configuration * file (INET_IPSECALGSFILE.) */ tmp_name = mktemp(tmp_name_template); fd = open(tmp_name, O_WRONLY|O_CREAT|O_EXCL, CFG_PERMS); if (fd == -1) { rc = LIBIPSEC_ALGS_DIAG_ALGSFILEOPEN; goto bail; } f = fdopen(fd, "w"); if (f == NULL) { (void) close(fd); rc = LIBIPSEC_ALGS_DIAG_ALGSFILEFDOPEN; goto bail; } FPUT_ERR(fputs(preamble, f)); /* Write protocol entries. */ for (i = 0; i < num_protos; i++) { /* add package section delimiters if needed */ rc = pkg_section(f, protos[i].proto_pkg, &doing_pkg, cur_pkg); if (rc != 0) goto bail; FPRINTF_ERR(fprintf(f, "%s%d|%s|", LIBIPSEC_ALGS_LINE_PROTO, protos[i].proto_num, protos[i].proto_name)); switch (protos[i].proto_exec_mode) { case LIBIPSEC_ALGS_EXEC_SYNC: FPRINTF_ERR(fprintf(f, "sync\n")); break; case LIBIPSEC_ALGS_EXEC_ASYNC: FPRINTF_ERR(fprintf(f, "async\n")); break; } } /* terminate the package section for the protocols if needed */ PKG_SEC_END(doing_pkg, cur_pkg); FPUT_ERR(fputs("\n", f)); /* Write algorithm entries. */ for (i = 0; i < num_protos; i++) { for (j = 0; j < protos[i].proto_numalgs; j++) { alg = protos[i].proto_algs[j]; /* add package section delimiters if needed */ alg_pkg = alg_has_pkg(&protos[i], alg); rc = pkg_section(f, alg_pkg, &doing_pkg, cur_pkg); if (rc != 0) goto bail; /* protocol and algorithm numbers */ FPRINTF_ERR(fprintf(f, "%s%d|%d|", LIBIPSEC_ALGS_LINE_ALG, alg->a_proto_num, alg->a_alg_num)); /* algorithm names */ for (k = 0; alg->a_names[k] != NULL; k++) { FPRINTF_ERR(fprintf(f, "%s", alg->a_names[k])); if (alg->a_names[k+1] != NULL) FPRINTF_ERR(fprintf(f, ",")); } /* mechanism name */ FPRINTF_ERR(fprintf(f, "|%s|", alg->a_mech_name)); /* key sizes */ if (alg->a_key_increment == 0) { /* key sizes defined by enumeration */ if (list_ints(f, alg->a_key_sizes) == -1) { rc = LIBIPSEC_ALGS_DIAG_ALGSFILEWRITE; goto bail; } } else { /* key sizes defined by range */ FPRINTF_ERR(fprintf(f, "%d/%d-%d,%d", alg->a_key_sizes[0], alg->a_key_sizes[1], alg->a_key_sizes[2], alg->a_key_increment)); } FPUT_ERR(fputc('|', f)); /* block sizes */ if (list_ints(f, alg->a_block_sizes) == -1) { rc = LIBIPSEC_ALGS_DIAG_ALGSFILEWRITE; goto bail; } FPUT_ERR(fputc('|', f)); /* * Some algorithms require extra parameters, these * are stored in an array. For algorithms that don't * need these parameters, or flags (below), these * extra fields in the ipsecalgs file must contain a * zero. This fuction will get called if a algorithm * entry is added, at this point the extra fields will * be added to the file. */ if (list_ints(f, alg->a_mech_params) == -1) { rc = LIBIPSEC_ALGS_DIAG_ALGSFILEWRITE; goto bail; } /* flags */ FPRINTF_ERR(fprintf(f, "|%d\n", alg->a_alg_flags)); } } /* terminate the package section for the algorithms if needed */ PKG_SEC_END(doing_pkg, cur_pkg); if (fchmod(fd, CFG_PERMS) == -1) { rc = LIBIPSEC_ALGS_DIAG_ALGSFILECHMOD; goto bail; } if (fchown(fd, CFG_OWNER, CFG_GROUP) == -1) { rc = LIBIPSEC_ALGS_DIAG_ALGSFILECHOWN; goto bail; } if (fclose(f) == EOF) { rc = LIBIPSEC_ALGS_DIAG_ALGSFILECLOSE; goto bail; } if (rename(tmp_name, INET_IPSECALGSFILE) == -1) rc = LIBIPSEC_ALGS_DIAG_ALGSFILERENAME; bail: _clean_trash(protos, num_protos); return (rc); } /* * Return a pointer to the protocol entry corresponding to the specified * protocol num proto_num. Also builds the list of currently defined * protocols. */ static ipsec_proto_t * proto_setup(ipsec_proto_t **protos, int *num_protos, int proto_num, boolean_t cleanup) { int i; ipsec_proto_t *current_proto, *ret_proto = NULL; _build_internal_algs(protos, num_protos); if (*protos == NULL) return (NULL); for (i = 0; i < *num_protos; i++) { current_proto = (*protos) + i; if (current_proto->proto_num == proto_num) { ret_proto = current_proto; break; } } if (ret_proto == NULL) { if (cleanup) _clean_trash(*protos, *num_protos); /* else caller wants parsed /etc/inet/ipsecalgs anyway */ } return (ret_proto); } /* * Delete the first found algorithm of the specified protocol which * has the same name as the one specified by alg_name. Deletion of * the entry takes place only if the delete_it flag is set. If an * entry was found, return B_TRUE, otherwise return B_FALSE. */ static boolean_t delipsecalgbyname_common(const char *name, ipsec_proto_t *proto, boolean_t delete_it) { int i; char **name_check; boolean_t found_match = B_FALSE; for (i = 0; i < proto->proto_numalgs; i++) { if (!found_match) { for (name_check = proto->proto_algs[i]->a_names; *name_check != NULL; name_check++) { /* * Can use strcmp because the algorithm names * are bound. */ if (strcmp(*name_check, name) == 0) { found_match = B_TRUE; if (!delete_it) return (found_match); freeipsecalgent(proto->proto_algs[i]); break; } } } else { proto->proto_algs[i - 1] = proto->proto_algs[i]; } } if (found_match) proto->proto_numalgs--; return (found_match); } /* * Returns B_TRUE if the specified 0-terminated lists of key or * block sizes match, B_FALSE otherwise. */ static boolean_t sizes_match(int *a1, int *a2) { int i; for (i = 0; (a1[i] != 0) && (a2[i] != 0); i++) { if (a1[i] != a2[i]) return (B_FALSE); } if ((a1[i] != 0) || (a2[i] != 0)) return (B_FALSE); return (B_TRUE); } /* * Returns B_TRUE if an _exact_ equivalent of the specified algorithm * already exists, B_FALSE otherwise. */ static boolean_t ipsecalg_exists(struct ipsecalgent *newbie, ipsec_proto_t *proto) { struct ipsecalgent *curalg; char **curname, **newbiename; int i; boolean_t match; for (i = 0; i < proto->proto_numalgs; i++) { curalg = proto->proto_algs[i]; if (curalg->a_alg_num != newbie->a_alg_num) continue; if (curalg->a_key_increment != newbie->a_key_increment) continue; if (strcmp(curalg->a_mech_name, newbie->a_mech_name) != 0) continue; curname = curalg->a_names; newbiename = newbie->a_names; match = B_TRUE; while ((*curname != NULL) && (*newbiename != NULL) && match) { match = (strcmp(*curname, *newbiename) == 0); curname++; newbiename++; } if (!match || (*curname != NULL) || (*newbiename != NULL)) continue; if (!sizes_match(curalg->a_block_sizes, newbie->a_block_sizes)) continue; if (!sizes_match(curalg->a_key_sizes, newbie->a_key_sizes)) continue; /* we found an exact match */ return (B_TRUE); } return (B_FALSE); } /* * Add a new algorithm to the /etc/inet/ipsecalgs file. Caller must free * or otherwise address "newbie". */ int addipsecalg(struct ipsecalgent *newbie, uint_t flags) { ipsec_proto_t *protos, *current_proto; struct ipsecalgent *clone, **holder; int num_protos, i; char **name_check; boolean_t forced_add = (flags & LIBIPSEC_ALGS_ADD_FORCE) != 0; boolean_t found_match; if ((current_proto = proto_setup(&protos, &num_protos, newbie->a_proto_num, B_TRUE)) == NULL) return (LIBIPSEC_ALGS_DIAG_UNKN_PROTO); /* * If an algorithm that matches _exactly_ the new algorithm * already exists, we're done. */ if (ipsecalg_exists(newbie, current_proto)) return (0); /* * We don't allow a new algorithm to be created if one of * its names is already defined for an existing algorithm, * unless the operation is forced, in which case existing * algorithm entries that conflict with the new one are * deleted. */ for (name_check = newbie->a_names; *name_check != NULL; name_check++) { found_match = delipsecalgbyname_common(*name_check, current_proto, forced_add); if (found_match && !forced_add) { /* * Duplicate entry found, but the addition was * not forced. */ _clean_trash(protos, num_protos); return (LIBIPSEC_ALGS_DIAG_ALG_EXISTS); } } for (i = 0; i < current_proto->proto_numalgs; i++) { if (current_proto->proto_algs[i]->a_alg_num == newbie->a_alg_num) { /* * An algorithm with the same protocol number * and algorithm number already exists. Fail * addition unless the operation is forced. */ if (flags & LIBIPSEC_ALGS_ADD_FORCE) { clone = _duplicate_alg(newbie); if (clone != NULL) { freeipsecalgent( current_proto->proto_algs[i]); current_proto->proto_algs[i] = clone; return (write_new_algfile(protos, num_protos)); } else { _clean_trash(protos, num_protos); return (LIBIPSEC_ALGS_DIAG_NOMEM); } } else { _clean_trash(protos, num_protos); return (LIBIPSEC_ALGS_DIAG_ALG_EXISTS); } } } /* append the new algorithm */ holder = realloc(current_proto->proto_algs, sizeof (struct ipsecalgent *) * (i + 1)); if (holder == NULL) { _clean_trash(protos, num_protos); return (LIBIPSEC_ALGS_DIAG_NOMEM); } clone = _duplicate_alg(newbie); if (clone == NULL) { free(holder); _clean_trash(protos, num_protos); return (LIBIPSEC_ALGS_DIAG_NOMEM); } current_proto->proto_numalgs++; current_proto->proto_algs = holder; current_proto->proto_algs[i] = clone; return (write_new_algfile(protos, num_protos)); } /* * Delete an algorithm by name & protocol number from /etc/inet/ipsecalgs. * Only deletes the first encountered instance. */ int delipsecalgbyname(const char *name, int proto_num) { ipsec_proto_t *protos, *current_proto; int num_protos; if ((current_proto = proto_setup(&protos, &num_protos, proto_num, B_TRUE)) == NULL) return (LIBIPSEC_ALGS_DIAG_UNKN_PROTO); if (delipsecalgbyname_common(name, current_proto, B_TRUE)) return (write_new_algfile(protos, num_protos)); _clean_trash(protos, num_protos); return (LIBIPSEC_ALGS_DIAG_UNKN_ALG); } /* * Delete an algorithm by num + protocol num from /etc/inet/ipsecalgs. */ int delipsecalgbynum(int alg_num, int proto_num) { ipsec_proto_t *protos, *current_proto; int i, num_protos; boolean_t found_match = B_FALSE; if ((current_proto = proto_setup(&protos, &num_protos, proto_num, B_TRUE)) == NULL) return (LIBIPSEC_ALGS_DIAG_UNKN_PROTO); for (i = 0; i < current_proto->proto_numalgs; i++) { if (!found_match) { if (current_proto->proto_algs[i]->a_alg_num == alg_num) { found_match = B_TRUE; freeipsecalgent(current_proto->proto_algs[i]); } } else { current_proto->proto_algs[i - 1] = current_proto->proto_algs[i]; } } if (found_match) { current_proto->proto_numalgs--; return (write_new_algfile(protos, num_protos)); } _clean_trash(protos, num_protos); return (LIBIPSEC_ALGS_DIAG_UNKN_ALG); } /* * Remove the specified protocol entry from the list of protocols. */ static void delipsecproto_common(ipsec_proto_t *protos, int num_protos, ipsec_proto_t *proto) { int i; /* free protocol storage */ free(proto->proto_name); for (i = 0; i < proto->proto_numalgs; i++) freeipsecalgent(proto->proto_algs[i]); /* remove from list of prototocols */ for (i = (proto - protos + 1); i < num_protos; i++) protos[i - 1] = protos[i]; } /* * Add an IPsec protocol to /etc/inet/ipsecalgs. */ int addipsecproto(const char *proto_name, int proto_num, ipsecalgs_exec_mode_t proto_exec_mode, uint_t flags) { ipsec_proto_t *protos, *current_proto, *new_proto; int i, num_protos; /* * NOTE:If build_internal_algs returns NULL for any * reason, we will end up clobbering /etc/inet/ipsecalgs! */ current_proto = proto_setup(&protos, &num_protos, proto_num, B_FALSE); /* check for protocol with duplicate id */ if (current_proto != NULL) { if ((strcmp(proto_name, current_proto->proto_name) == 0) && (proto_exec_mode == current_proto->proto_exec_mode)) { /* * The current protocol being added matches * exactly an existing protocol, we're done. */ return (0); } if (!(flags & LIBIPSEC_ALGS_ADD_FORCE)) return (LIBIPSEC_ALGS_DIAG_PROTO_EXISTS); delipsecproto_common(protos, num_protos--, current_proto); } /* check for protocol with duplicate name */ for (i = 0; i < num_protos; i++) { if (strcmp(protos[i].proto_name, proto_name) == 0) { if (!(flags & LIBIPSEC_ALGS_ADD_FORCE)) return (LIBIPSEC_ALGS_DIAG_PROTO_EXISTS); delipsecproto_common(protos, num_protos--, &protos[i]); break; } } /* add new protocol */ num_protos++; new_proto = realloc(protos, num_protos * sizeof (ipsec_proto_t)); if (new_proto == NULL) { _clean_trash(protos, num_protos - 1); return (LIBIPSEC_ALGS_DIAG_NOMEM); } protos = new_proto; new_proto += (num_protos - 1); /* initialize protocol entry */ new_proto->proto_num = proto_num; new_proto->proto_numalgs = 0; new_proto->proto_algs = NULL; new_proto->proto_name = strdup(proto_name); if (new_proto->proto_name == NULL) { _clean_trash(protos, num_protos); return (LIBIPSEC_ALGS_DIAG_NOMEM); } new_proto->proto_pkg = NULL; new_proto->proto_algs_pkgs = NULL; new_proto->proto_algs_npkgs = 0; new_proto->proto_exec_mode = proto_exec_mode; return (write_new_algfile(protos, num_protos)); } /* * Delete an IPsec protocol entry from /etc/inet/ipsecalgs. This also * nukes the associated algorithms. */ int delipsecprotobynum(int proto_num) { ipsec_proto_t *protos, *current_proto; int num_protos; if ((current_proto = proto_setup(&protos, &num_protos, proto_num, B_TRUE)) == NULL) return (LIBIPSEC_ALGS_DIAG_UNKN_PROTO); delipsecproto_common(protos, num_protos--, current_proto); return (write_new_algfile(protos, num_protos)); } int delipsecprotobyname(const char *proto_name) { int proto_num; proto_num = getipsecprotobyname(proto_name); if (proto_num == -1) return (LIBIPSEC_ALGS_DIAG_UNKN_PROTO); return (delipsecprotobynum(proto_num)); } /* * Implement these in libnsl since these are read-only operations. */ int * getipsecprotos(int *nentries) { return (_real_getipsecprotos(nentries)); } int * getipsecalgs(int *nentries, int proto_num) { return (_real_getipsecalgs(nentries, proto_num)); } const char * ipsecalgs_diag(int diag) { switch (diag) { case LIBIPSEC_ALGS_DIAG_ALG_EXISTS: return (dgettext(TEXT_DOMAIN, "Algorithm already exists")); case LIBIPSEC_ALGS_DIAG_PROTO_EXISTS: return (dgettext(TEXT_DOMAIN, "Protocol already exists")); case LIBIPSEC_ALGS_DIAG_UNKN_PROTO: return (dgettext(TEXT_DOMAIN, "Unknown protocol")); case LIBIPSEC_ALGS_DIAG_UNKN_ALG: return (dgettext(TEXT_DOMAIN, "Unknown algorithm")); case LIBIPSEC_ALGS_DIAG_NOMEM: return (dgettext(TEXT_DOMAIN, "Out of memory")); case LIBIPSEC_ALGS_DIAG_ALGSFILEOPEN: return (dgettext(TEXT_DOMAIN, "open() failed")); case LIBIPSEC_ALGS_DIAG_ALGSFILEFDOPEN: return (dgettext(TEXT_DOMAIN, "fdopen() failed")); case LIBIPSEC_ALGS_DIAG_ALGSFILELOCK: return (dgettext(TEXT_DOMAIN, "lockf() failed")); case LIBIPSEC_ALGS_DIAG_ALGSFILERENAME: return (dgettext(TEXT_DOMAIN, "rename() failed")); case LIBIPSEC_ALGS_DIAG_ALGSFILEWRITE: return (dgettext(TEXT_DOMAIN, "write to file failed")); case LIBIPSEC_ALGS_DIAG_ALGSFILECHMOD: return (dgettext(TEXT_DOMAIN, "chmod() failed")); case LIBIPSEC_ALGS_DIAG_ALGSFILECHOWN: return (dgettext(TEXT_DOMAIN, "chown() failed")); case LIBIPSEC_ALGS_DIAG_ALGSFILECLOSE: return (dgettext(TEXT_DOMAIN, "close() failed")); default: return (dgettext(TEXT_DOMAIN, "failed")); } } /* * Get the execution mode corresponding to the specified protocol. * Returns 0 on success, one of the LIBIPSEC_ALGS_DIAG_* values on * failure. */ int ipsecproto_get_exec_mode(int proto_num, ipsecalgs_exec_mode_t *exec_mode) { ipsec_proto_t *protos, *current_proto; int num_protos; if ((current_proto = proto_setup(&protos, &num_protos, proto_num, B_TRUE)) == NULL) return (LIBIPSEC_ALGS_DIAG_UNKN_PROTO); *exec_mode = current_proto->proto_exec_mode; _clean_trash(protos, num_protos); return (0); } /* * Set the execution mode of the specified protocol. Returns 0 on success, * or one of the LIBIPSEC_ALGS_DIAG_* values on failure. */ int ipsecproto_set_exec_mode(int proto_num, ipsecalgs_exec_mode_t exec_mode) { ipsec_proto_t *protos, *current_proto; int num_protos; if ((current_proto = proto_setup(&protos, &num_protos, proto_num, B_TRUE)) == NULL) return (LIBIPSEC_ALGS_DIAG_UNKN_PROTO); current_proto->proto_exec_mode = exec_mode; return (write_new_algfile(protos, num_protos)); } /* * 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 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _ERRFP_H #define _ERRFP_H /* * Headers and definitions for support functions that are shared by * the ipsec utilities ipseckey and ikeadm. */ #ifdef __cplusplus extern "C" { #endif #include #include /* * Function Prototypes */ #ifdef __PRAGMA_REDEFINE_EXTNAME #pragma redefine_extname errfp _errfp #pragma redefine_extname verrfp _verrfp #pragma redefine_extname errxfp _errxfp #pragma redefine_extname verrxfp _verrxfp #pragma redefine_extname warnfp _warnfp #pragma redefine_extname vwarnfp _vwarnfp #pragma redefine_extname warnxfp _warnxfp #pragma redefine_extname vwarnxfp _vwarnxfp #else #define errfp _errfp #define verrfp _verrfp #define errxfp _errxfp #define verrxfp _verrxfp #define warnfp _warnfp #define vwarnfp _vwarnfp #define warnxfp _warnxfp #define vwarnxfp _vwarnxfp #endif /* Program exit and warning calls */ extern void errfp(FILE *, int, const char *, ...); extern void verrfp(FILE *, int, const char *, va_list); extern void errxfp(FILE *, int, const char *, ...); extern void verrxfp(FILE *, int, const char *, va_list); extern void warnfp(FILE *, const char *, ...); extern void vwarnfp(FILE *, const char *, va_list); extern void warnxfp(FILE *, const char *, ...); extern void vwarnxfp(FILE *, const char *, va_list); #ifdef __cplusplus } #endif #endif /* _ERRFP_H */ /* * 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. */ #ifndef _IKEDOOR_H #define _IKEDOOR_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include /* * This version number is intended to stop the calling process from * getting confused if a structure is changed and a mismatch occurs. * This should be incremented each time a structure is changed. */ /* * The IKE process may be a 64-bit process, but ikeadm or any other IKE * door consumer does not have to be. We need to be strict ala. PF_KEY or * any on-the-wire-protocol with respect to structure fields offsets and * alignment. Please make sure all structures are the same size on both * 64-bit and 32-bit execution environments (or even other ones), and that * apart from trivial 4-byte enums or base headers, that all structures are * multiples of 8-bytes (64-bits). */ #define DOORVER 4 #define DOORNM "/var/run/ike_door" typedef enum { IKE_SVC_GET_DBG, IKE_SVC_SET_DBG, IKE_SVC_GET_PRIV, IKE_SVC_SET_PRIV, IKE_SVC_GET_STATS, IKE_SVC_GET_P1, IKE_SVC_DEL_P1, IKE_SVC_DUMP_P1S, IKE_SVC_FLUSH_P1S, IKE_SVC_GET_RULE, IKE_SVC_NEW_RULE, IKE_SVC_DEL_RULE, IKE_SVC_DUMP_RULES, IKE_SVC_READ_RULES, IKE_SVC_WRITE_RULES, IKE_SVC_GET_PS, IKE_SVC_NEW_PS, IKE_SVC_DEL_PS, IKE_SVC_DUMP_PS, IKE_SVC_READ_PS, IKE_SVC_WRITE_PS, IKE_SVC_DBG_RBDUMP, IKE_SVC_GET_DEFS, IKE_SVC_SET_PIN, IKE_SVC_DEL_PIN, IKE_SVC_DUMP_CERTCACHE, IKE_SVC_FLUSH_CERTCACHE, IKE_SVC_DUMP_GROUPS, IKE_SVC_DUMP_ENCRALGS, IKE_SVC_DUMP_AUTHALGS, IKE_SVC_ERROR } ike_svccmd_t; /* DPD status */ typedef enum dpd_status { DPD_NOT_INITIATED = 0, DPD_IN_PROGRESS, DPD_SUCCESSFUL, DPD_FAILURE } dpd_status_t; #define IKE_SVC_MAX IKE_SVC_ERROR /* * Support structures/defines */ #define IKEDOORROUNDUP(i) P2ROUNDUP((i), sizeof (uint64_t)) /* * Debug categories. The debug level is a bitmask made up of * flags indicating the desired categories; only 31 bits are * available, as the highest-order bit designates an invalid * setting. */ #define D_INVALID 0x80000000 #define D_CERT 0x00000001 /* certificate management */ #define D_KEY 0x00000002 /* key management */ #define D_OP 0x00000004 /* operational: config, init, mem */ #define D_P1 0x00000008 /* phase 1 negotiation */ #define D_P2 0x00000010 /* phase 2 negotiation */ #define D_PFKEY 0x00000020 /* pf key interface */ #define D_POL 0x00000040 /* policy management */ #define D_PROP 0x00000080 /* proposal construction */ #define D_DOOR 0x00000100 /* door server */ #define D_CONFIG 0x00000200 /* config file processing */ #define D_LABEL 0x00000400 /* MAC labels */ #define D_HIGHBIT 0x00000400 #define D_ALL 0x000007ff /* * Access privilege levels: define level of access to keying information. * The privileges granted at each level is a superset of the privileges * granted at all lower levels. * * The door operations which require special privileges are: * * - receiving keying material for SAs and preshared key entries * IKE_PRIV_KEYMAT must be set for this. * * - get/dump/new/delete/read/write preshared keys * IKE_PRIV_KEYMAT or IKE_PRIV_MODKEYS must be set to do this. * If IKE_PRIV_MODKEYS is set, the information returned for a * get/dump request will not include the actual key; in order * to get the key itself, IKE_PRIV_KEYMAT must be set. * * - modifying the privilege level: the daemon's privilege level * is set when the daemon is started; the level may only be * lowered via the door interface. * * All other operations are allowed at any privilege level. */ #define IKE_PRIV_MINIMUM 0 #define IKE_PRIV_MODKEYS 1 #define IKE_PRIV_KEYMAT 2 #define IKE_PRIV_MAXIMUM 2 /* global ike stats formatting structure */ typedef struct { uint32_t st_init_p1_current; uint32_t st_resp_p1_current; uint32_t st_init_p1_total; uint32_t st_resp_p1_total; uint32_t st_init_p1_attempts; uint32_t st_resp_p1_attempts; uint32_t st_init_p1_noresp; /* failed; no response from peer */ uint32_t st_init_p1_respfail; /* failed, but peer responded */ uint32_t st_resp_p1_fail; uint32_t st_reserved; char st_pkcs11_libname[PATH_MAX]; } ike_stats_t; /* structure used to pass default values used by in.iked back to ikeadm */ typedef struct { uint32_t rule_p1_lifetime_secs; uint32_t rule_p1_minlife; uint32_t rule_p1_nonce_len; uint32_t rule_p2_lifetime_secs; uint32_t rule_p2_softlife_secs; uint32_t rule_p2_idletime_secs; uint32_t sys_p2_lifetime_secs; uint32_t sys_p2_softlife_secs; uint32_t sys_p2_idletime_secs; uint32_t rule_p2_lifetime_kb; uint32_t rule_p2_softlife_kb; uint32_t sys_p2_lifetime_bytes; uint32_t sys_p2_softlife_bytes; uint32_t rule_p2_minlife_hard_secs; uint32_t rule_p2_minlife_soft_secs; uint32_t rule_p2_minlife_idle_secs; uint32_t rule_p2_minlife_hard_kb; uint32_t rule_p2_minlife_soft_kb; uint32_t rule_p2_maxlife_secs; uint32_t rule_p2_maxlife_kb; uint32_t rule_p2_nonce_len; uint32_t rule_p2_pfs; uint32_t rule_p2_mindiff_secs; uint32_t rule_p2_mindiff_kb; uint32_t conversion_factor; /* for secs to kbytes */ uint32_t rule_max_certs; uint32_t rule_ike_port; uint32_t rule_natt_port; uint32_t defaults_reserved; /* For 64-bit alignment. */ } ike_defaults_t; /* data formatting structures for P1 SA dumps */ typedef struct { struct sockaddr_storage loc_addr; struct sockaddr_storage rem_addr; #define beg_iprange loc_addr #define end_iprange rem_addr } ike_addr_pr_t; typedef struct { uint64_t cky_i; uint64_t cky_r; } ike_cky_pr_t; typedef struct { ike_cky_pr_t p1hdr_cookies; uint8_t p1hdr_major; uint8_t p1hdr_minor; uint8_t p1hdr_xchg; uint8_t p1hdr_isinit; uint32_t p1hdr_state; boolean_t p1hdr_support_dpd; dpd_status_t p1hdr_dpd_state; uint64_t p1hdr_dpd_time; } ike_p1_hdr_t; /* values for p1hdr_xchg (aligned with RFC2408, section 3.1) */ #define IKE_XCHG_NONE 0 #define IKE_XCHG_BASE 1 #define IKE_XCHG_IDENTITY_PROTECT 2 #define IKE_XCHG_AUTH_ONLY 3 #define IKE_XCHG_AGGRESSIVE 4 /* following not from RFC; used only for preshared key definitions */ #define IKE_XCHG_IP_AND_AGGR 240 /* also not from RFC; used as wildcard */ #define IKE_XCHG_ANY 256 /* values for p1hdr_state */ #define IKE_SA_STATE_INVALID 0 #define IKE_SA_STATE_INIT 1 #define IKE_SA_STATE_SENT_SA 2 #define IKE_SA_STATE_SENT_KE 3 #define IKE_SA_STATE_SENT_LAST 4 #define IKE_SA_STATE_DONE 5 #define IKE_SA_STATE_DELETED 6 typedef struct { uint16_t p1xf_dh_group; uint16_t p1xf_encr_alg; uint16_t p1xf_encr_low_bits; uint16_t p1xf_encr_high_bits; uint16_t p1xf_auth_alg; uint16_t p1xf_auth_meth; uint16_t p1xf_prf; uint16_t p1xf_pfs; uint32_t p1xf_max_secs; uint32_t p1xf_max_kbytes; uint32_t p1xf_max_keyuses; uint32_t p1xf_reserved; /* Alignment to 64-bit. */ } ike_p1_xform_t; /* values for p1xf_dh_group (aligned with RFC2409, Appendix A) */ #define IKE_GRP_DESC_MODP_768 1 #define IKE_GRP_DESC_MODP_1024 2 #define IKE_GRP_DESC_EC2N_155 3 #define IKE_GRP_DESC_EC2N_185 4 /* values for p1xf_dh_group (aligned with RFC3526) */ #define IKE_GRP_DESC_MODP_1536 5 #define IKE_GRP_DESC_MODP_2048 14 #define IKE_GRP_DESC_MODP_3072 15 #define IKE_GRP_DESC_MODP_4096 16 #define IKE_GRP_DESC_MODP_6144 17 #define IKE_GRP_DESC_MODP_8192 18 #define IKE_GRP_DESC_ECP_256 19 #define IKE_GRP_DESC_ECP_384 20 #define IKE_GRP_DESC_ECP_521 21 /* values for p1xf_dh_group (aligned with RFC5114) */ #define IKE_GRP_DESC_MODP_1024_160 22 #define IKE_GRP_DESC_MODP_2048_224 23 #define IKE_GRP_DESC_MODP_2048_256 24 #define IKE_GRP_DESC_ECP_192 25 #define IKE_GRP_DESC_ECP_224 26 /* values for p1xf_auth_meth (aligned with RFC2409, Appendix A) */ #define IKE_AUTH_METH_PRE_SHARED_KEY 1 #define IKE_AUTH_METH_DSS_SIG 2 #define IKE_AUTH_METH_RSA_SIG 3 #define IKE_AUTH_METH_RSA_ENCR 4 #define IKE_AUTH_METH_RSA_ENCR_REVISED 5 /* values for p1xf_prf */ #define IKE_PRF_NONE 0 #define IKE_PRF_HMAC_MD5 1 #define IKE_PRF_HMAC_SHA1 2 #define IKE_PRF_HMAC_SHA256 5 #define IKE_PRF_HMAC_SHA384 6 #define IKE_PRF_HMAC_SHA512 7 typedef struct { /* * NOTE: the new and del counters count the actual number of SAs, * not the number of "suites", as defined in the ike monitoring * mib draft; we do this because we don't have a good way of * tracking the deletion of entire suites (we're notified of * deleted qm sas individually). */ uint32_t p1stat_new_qm_sas; uint32_t p1stat_del_qm_sas; uint64_t p1stat_start; uint32_t p1stat_kbytes; uint32_t p1stat_keyuses; } ike_p1_stats_t; typedef struct { uint32_t p1err_decrypt; uint32_t p1err_hash; uint32_t p1err_otherrx; uint32_t p1err_tx; } ike_p1_errors_t; typedef struct { uint32_t p1key_type; uint32_t p1key_len; /* * followed by (len - sizeof (ike_p1_key_t)) bytes of hex data, * 64-bit aligned (pad bytes are added at the end, if necessary, * and NOT INCLUDED in the len value, which reflects the actual * key size). */ } ike_p1_key_t; /* key info types for ike_p1_key_t struct */ #define IKE_KEY_PRESHARED 1 #define IKE_KEY_SKEYID 2 #define IKE_KEY_SKEYID_D 3 #define IKE_KEY_SKEYID_A 4 #define IKE_KEY_SKEYID_E 5 #define IKE_KEY_ENCR 6 #define IKE_KEY_IV 7 typedef struct { ike_p1_hdr_t p1sa_hdr; ike_p1_xform_t p1sa_xform; ike_addr_pr_t p1sa_ipaddrs; uint16_t p1sa_stat_off; uint16_t p1sa_stat_len; uint16_t p1sa_error_off; uint16_t p1sa_error_len; uint16_t p1sa_localid_off; uint16_t p1sa_localid_len; uint16_t p1sa_remoteid_off; uint16_t p1sa_remoteid_len; uint16_t p1sa_key_off; uint16_t p1sa_key_len; uint32_t p1sa_reserved; /* * variable-length structures will be included here, as * indicated by offset/length fields. * stats and errors will be formatted as ike_p1_stats_t and * ike_p1_errors_t, respectively. * key info will be formatted as a series of p1_key_t structs. * local/remote ids will be formatted as sadb_ident_t structs. */ } ike_p1_sa_t; #define MAX_LABEL_LEN 256 /* data formatting structure for policy (rule) dumps */ typedef struct { char rule_label[MAX_LABEL_LEN]; uint32_t rule_kmcookie; uint16_t rule_ike_mode; uint16_t rule_local_idtype; /* SADB_IDENTTYPE_* value */ uint32_t rule_p1_nonce_len; uint32_t rule_p2_nonce_len; uint32_t rule_p2_pfs; uint32_t rule_p2_lifetime_secs; uint32_t rule_p2_softlife_secs; uint32_t rule_p2_idletime_secs; uint32_t rule_p2_lifetime_kb; uint32_t rule_p2_softlife_kb; uint16_t rule_xform_cnt; uint16_t rule_xform_off; uint16_t rule_locip_cnt; uint16_t rule_locip_off; uint16_t rule_remip_cnt; uint16_t rule_remip_off; uint16_t rule_locid_inclcnt; uint16_t rule_locid_exclcnt; uint16_t rule_locid_off; uint16_t rule_remid_inclcnt; uint16_t rule_remid_exclcnt; uint16_t rule_remid_off; /* * Followed by several lists of variable-length structures, described * by counts and offsets: * transforms ike_p1_xform_t structs * ranges of local ip addrs ike_addr_pr_t structs * ranges of remote ip addrs ike_addr_pr_t structs * local identification strings null-terminated ascii strings * remote identification strings null-terminated ascii strings */ } ike_rule_t; /* data formatting structure for DH group dumps */ typedef struct { uint16_t group_number; uint16_t group_bits; char group_label[MAX_LABEL_LEN]; } ike_group_t; /* data formatting structure for encryption algorithm dumps */ typedef struct { uint_t encr_value; char encr_name[MAX_LABEL_LEN]; int encr_keylen_min; int encr_keylen_max; } ike_encralg_t; /* data formatting structure for authentication algorithm dumps */ typedef struct { uint_t auth_value; char auth_name[MAX_LABEL_LEN]; } ike_authalg_t; /* * data formatting structure for preshared keys * ps_ike_mode field uses the IKE_XCHG_* defs */ typedef struct { ike_addr_pr_t ps_ipaddrs; uint16_t ps_ike_mode; uint16_t ps_localid_off; uint16_t ps_localid_len; uint16_t ps_remoteid_off; uint16_t ps_remoteid_len; uint16_t ps_key_off; uint16_t ps_key_len; uint16_t ps_key_bits; int ps_localid_plen; int ps_remoteid_plen; /* * followed by variable-length structures, as indicated by * offset/length fields. * key info will be formatted as an array of bytes. * local/remote ids will be formatted as sadb_ident_t structs. */ } ike_ps_t; #define DN_MAX 1024 #define CERT_OFF_WIRE -1 #define CERT_NO_PRIVKEY 0 #define CERT_PRIVKEY_LOCKED 1 #define CERT_PRIVKEY_AVAIL 2 /* * data formatting structure for cached certs */ typedef struct { uint32_t cache_id; uint32_t certclass; int linkage; uint32_t certcache_padding; /* For 64-bit alignment. */ char subject[DN_MAX]; char issuer[DN_MAX]; } ike_certcache_t; /* identification types */ #define IKE_ID_IDENT_PAIR 1 #define IKE_ID_ADDR_PAIR 2 #define IKE_ID_CKY_PAIR 3 #define IKE_ID_LABEL 4 /* locations for read/write requests */ #define IKE_RW_LOC_DEFAULT 1 #define IKE_RW_LOC_USER_SPEC 2 /* door interface error codes */ #define IKE_ERR_NO_OBJ 1 /* nothing found to match the request */ #define IKE_ERR_NO_DESC 2 /* fd was required with this request */ #define IKE_ERR_ID_INVALID 3 /* invalid id info was provided */ #define IKE_ERR_LOC_INVALID 4 /* invalid location info was provided */ #define IKE_ERR_CMD_INVALID 5 /* invalid command was provided */ #define IKE_ERR_DATA_INVALID 6 /* invalid data was provided */ #define IKE_ERR_CMD_NOTSUP 7 /* unsupported command */ #define IKE_ERR_REQ_INVALID 8 /* badly formatted request */ #define IKE_ERR_NO_PRIV 9 /* privilege level not high enough */ #define IKE_ERR_SYS_ERR 10 /* syserr occurred while processing */ #define IKE_ERR_DUP_IGNORED 11 /* attempt to add a duplicate entry */ #define IKE_ERR_NO_TOKEN 12 /* cannot login into pkcs#11 token */ #define IKE_ERR_NO_AUTH 13 /* not authorized */ #define IKE_ERR_IN_PROGRESS 14 /* operation already in progress */ #define IKE_ERR_NO_MEM 15 /* insufficient memory */ /* * IKE_SVC_GET_DBG * Used to request the current debug level. * * Upon request, dbg_level is 0 (don't care). * * Upon return, dbg_level contains the current value. * * * IKE_SVC_SET_DBG * Used to request modification of the debug level. * * Upon request, dbg_level contains desired level. If debug output is * to be directed to a different file, the fd should be passed in the * door_desc_t field of the door_arg_t param. NOTE: if the daemon is * currently running in the background with no debug set, an output * file MUST be given. * * Upon return, dbg_level contains the old debug level, and acknowledges * successful completion of the request. If an error is encountered, * ike_err_t is returned instead, with appropriate error value and cmd * IKE_SVC_ERROR. */ typedef struct { ike_svccmd_t cmd; uint32_t dbg_level; } ike_dbg_t; /* * IKE_SVC_GET_PRIV * Used to request the current privilege level. * * Upon request, priv_level is 0 (don't care). * * Upon return, priv_level contains the current value. * * * IKE_SVC_SET_PRIV * Used to request modification of the privilege level. * * Upon request, priv_level contains the desired level. The level may * only be lowered via the door interface; it cannot be raised. Thus, * if in.iked is started at the lowest level, it cannot be changed. * * Upon return, priv_level contains the old privilege level, and * acknowledges successful completion of the request. If an error is * encountered, ike_err_t is returned instead, with appropriate error * value and cmd IKE_SVC_ERROR. */ typedef struct { ike_svccmd_t cmd; uint32_t priv_level; } ike_priv_t; /* * IKE_SVC_GET_STATS * Used to request current statistics on Phase 1 SA creation and * failures. The statistics represent all activity in in.iked. * * Upon request, cmd is set, and stat_len does not matter. * * Upon successful return, stat_len contains the total size of the * returned buffer, which contains first the ike_statreq_t struct, * followed by the stat data in the ike_stats_t structure. In case * of an error in processing the request, ike_err_t is returned with * IKE_SVC_ERROR command and appropriate error code. */ typedef struct { ike_svccmd_t cmd; uint32_t stat_len; } ike_statreq_t; /* * IKE_SVC_GET_DEFS * Used to request default values from in.iked. * * Upon request, cmd is set, and stat_len does not matter. * * Upon successful return, stat_len contains the total size of the * returned buffer, this contains a pair of ike_defaults_t's. */ typedef struct { ike_svccmd_t cmd; uint32_t stat_len; uint32_t version; uint32_t defreq_reserved; /* For 64-bit alignment. */ } ike_defreq_t; /* * IKE_SVC_DUMP_{P1S|RULES|PS|CERTCACHE} * Used to request a table dump, and to return info for a single table * item. The expectation is that all of the table data will be passed * through the door, one entry at a time; an individual request must be * sent for each entry, however (the door server can't send unrequested * data). * * Upon request: cmd is set, and dump_next contains the item number * requested (0 for first request). dump_len is 0; no data follows. * * Upon return: cmd is set, and dump_next contains the item number of * the *next* item in the table (to be used in the subsequent request). * dump_next = 0 indicates that this is the last item in the table. * dump_len is the total length (data + struct) returned. Data is * formatted as indicated by the cmd type: * IKE_SVC_DUMP_P1S: ike_p1_sa_t * IKE_SVC_DUMP_RULES: ike_rule_t * IKE_SVC_DUMP_PS: ike_ps_t * IKE_SVC_DUMP_CERTCACHE: ike_certcache_t */ typedef struct { ike_svccmd_t cmd; uint32_t dump_len; union { struct { uint32_t dump_unext; uint32_t dump_ureserved; } dump_actual; uint64_t dump_alignment; } dump_u; #define dump_next dump_u.dump_actual.dump_unext #define dump_reserved dump_u.dump_actual.dump_ureserved /* dump_len - sizeof (ike_dump_t) bytes of data included here */ } ike_dump_t; /* * IKE_SVC_GET_{P1|RULE|PS} * Used to request and return individual table items. * * Upon request: get_len is the total msg length (struct + id data); * get_idtype indicates the type of identification being used. * IKE_SVC_GET_P1: ike_addr_pr_t or ike_cky_pr_t * IKE_SVC_GET_RULE: char string (label) * IKE_SVC_GET_PS: ike_addr_pr_t or pair of sadb_ident_t * * Upon return: get_len is the total size (struct + data), get_idtype * is unused, and the data that follows is formatted according to cmd: * IKE_SVC_GET_P1: ike_p1_sa_t * IKE_SVC_GET_RULE: ike_rule_t * IKE_SVC_GET_PS: ike_ps_t */ typedef struct { ike_svccmd_t cmd; uint32_t get_len; union { struct { uint32_t getu_idtype; uint32_t getu_reserved; } get_actual; uint64_t get_alignment; } get_u; #define get_idtype get_u.get_actual.getu_idtype #define get_reserved get_u.get_actual.getu_reserved /* get_len - sizeof (ike_get_t) bytes of data included here */ } ike_get_t; /* * IKE_SVC_NEW_{RULE|PS} * Used to request and acknowledge insertion of a table item. * * Upon request: new_len is the total (data + struct) size passed, or 0. * new_len = 0 => a door_desc_t is also included with a file descriptor * for a file containing the data to be added. The file should include * a single item: a rule, or a pre-shared key. For new_len != 0, the * data is formatted according to the cmd type: * IKE_SVC_NEW_RULE: ike_rule_t * IKE_SVC_NEW_PS: ike_ps_t * * Upon return: new_len is 0; simply acknowledges successful insertion * of the requested item. If insertion is not successful, ike_err_t is * returned instead with appropriate error value. */ typedef struct { ike_svccmd_t cmd; uint32_t new_len; /* new_len - sizeof (ike_new_t) bytes included here */ uint64_t new_align; /* Padding for 64-bit alignment. */ } ike_new_t; /* * IKE_SVC_DEL_{P1|RULE|PS} * Used to request and acknowledge the deletion of an individual table * item. * * Upon request: del_len is the total msg length (struct + id data); * del_idtype indicates the type of identification being used. * IKE_SVC_DEL_P1: ike_addr_pr_t or ike_cky_pr_t * IKE_SVC_DEL_RULE: char string (label) * IKE_SVC_DEL_PS: ike_addr_pr_t or pair of sadb_ident_t * * Upon return: acknowledges deletion of the requested item; del_len and * del_idtype are unspecified. If deletion is not successful, ike_err_t * is returned instead with appropriate error value. */ typedef struct { ike_svccmd_t cmd; uint32_t del_len; uint32_t del_idtype; uint32_t del_reserved; /* del_len - sizeof (ike_del_t) bytes of data included here. */ } ike_del_t; /* * IKE_SVC_READ_{RULES|PS} * Used to ask daemon to re-read particular configuration info. * * Upon request: rw_loc indicates where the info should be read from: * either from a user-supplied file descriptor(s), or from the default * location(s). If rw_loc indicates user-supplied location, the file * descriptor(s) should be passed in the door_desc_t struct. For the * IKE_SVC_READ_RULES cmd, two file descriptors should be specified: * first, one for the config file which contains the data to be read, * and second, one for the cookie file which will be written to as * in.iked process the config file. * * Upon return: rw_loc is unspecified; the message simply acknowledges * successful completion of the request. If an error occurred, * ike_err_t is returned instead with appropriate error value. * * * IKE_SVC_WRITE_{RULES|PS} * Used to ask daemon to write its current config info to files. * * Request and return are handled the same as for the IKE_SVC_READ_* * cmds; however, the rw_loc MUST be a user-supplied location. Also, * for the IKE_SVC_WRITE_RULES cmd, the cookie file fd is not required; * only a single fd, for the file to which the config info should be * written, should be passed in. */ typedef struct { ike_svccmd_t cmd; uint32_t rw_loc; } ike_rw_t; /* * IKE_SVC_FLUSH_P1S * IKE_SVC_FLUSH_CERTCACHE * * Used to request and acknowledge tear-down of all P1 SAs * or to flush the certificate cache. */ typedef struct { ike_svccmd_t cmd; } ike_flush_t; #ifndef PKCS11_TOKSIZE #define PKCS11_TOKSIZE 32 #endif #define MAX_PIN_LEN 256 /* * IKE_SVC_SET_PIN * IKE_SVC_DEL_PIN * * Used to supply a pin for a PKCS#11 tokenj object. * */ typedef struct { ike_svccmd_t cmd; uint32_t pin_reserved; /* For 64-bit alignment. */ char pkcs11_token[PKCS11_TOKSIZE]; uchar_t token_pin[MAX_PIN_LEN]; } ike_pin_t; /* * IKE_SVC_ERROR * Used on return if server encountered an error while processing * the request. An appropriate error code is included (as defined * in this header file); in the case of IKE_ERR_SYS_ERR, a value * from the UNIX errno space is included in the ike_err_unix field. */ typedef struct { ike_svccmd_t cmd; uint32_t ike_err; uint32_t ike_err_unix; uint32_t ike_err_reserved; } ike_err_t; /* * Generic type for use when the request/reply type is unknown */ typedef struct { ike_svccmd_t cmd; } ike_cmd_t; /* * Union containing all possible request/return structures. */ typedef union { ike_cmd_t svc_cmd; ike_dbg_t svc_dbg; ike_priv_t svc_priv; ike_statreq_t svc_stats; ike_dump_t svc_dump; ike_get_t svc_get; ike_new_t svc_new; ike_del_t svc_del; ike_rw_t svc_rw; ike_flush_t svc_flush; ike_pin_t svc_pin; ike_err_t svc_err; ike_defreq_t svc_defaults; } ike_service_t; #ifdef __cplusplus } #endif #endif /* _IKEDOOR_H */ /* * * 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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright 2012 Milan Juri. All rights reserved. * Copyright 2018 Joyent, Inc. * Copyright 2018 OmniOS Community Edition (OmniOSce) Association. * Copyright 2024 Oxide Computer Company * Copyright 2026 Edgecast Cloud LLC. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ipsec_util.h" #include "ikedoor.h" /* * This file contains support functions that are shared by the ipsec * utilities and daemons including ipseckey(8), ikeadm(8) and in.iked(8). */ #define EFD(file) (((file) == stdout) ? stderr : (file)) /* Limits for interactive mode. */ #define MAX_LINE_LEN IBUF_SIZE #define MAX_CMD_HIST 64000 /* in bytes */ /* Set standard default/initial values for globals... */ boolean_t pflag = B_FALSE; /* paranoid w.r.t. printing keying material */ boolean_t nflag = B_FALSE; /* avoid nameservice? */ boolean_t interactive = B_FALSE; /* util not running on cmdline */ boolean_t readfile = B_FALSE; /* cmds are being read from a file */ uint_t lineno = 0; /* track location if reading cmds from file */ uint_t lines_added = 0; uint_t lines_parsed = 0; jmp_buf env; /* for error recovery in interactive/readfile modes */ char *my_fmri = NULL; FILE *debugfile = stderr; static GetLine *gl = NULL; /* for interactive mode */ /* * Print errno and exit if cmdline or readfile, reset state if interactive * The error string *what should be dgettext()'d before calling bail(). */ void bail(char *what) { if (errno != 0) warn(what); else warnx(dgettext(TEXT_DOMAIN, "Error: %s"), what); if (readfile) { return; } if (interactive && !readfile) longjmp(env, 2); EXIT_FATAL(NULL); } /* * Print caller-supplied variable-arg error msg, then exit if cmdline or * readfile, or reset state if interactive. */ /*PRINTFLIKE1*/ void bail_msg(char *fmt, ...) { va_list ap; char msgbuf[BUFSIZ]; va_start(ap, fmt); (void) vsnprintf(msgbuf, BUFSIZ, fmt, ap); va_end(ap); if (readfile) warnx(dgettext(TEXT_DOMAIN, "ERROR on line %u:\n%s\n"), lineno, msgbuf); else warnx(dgettext(TEXT_DOMAIN, "ERROR: %s\n"), msgbuf); if (interactive && !readfile) longjmp(env, 1); EXIT_FATAL(NULL); } /* * bytecnt2str() wrapper. Zeroes out the input buffer and if the number * of bytes to be converted is more than 1K, it will produce readable string * in parentheses, store it in the original buffer and return the pointer to it. * Maximum length of the returned string is 14 characters (not including * the terminating zero). */ char * bytecnt2out(uint64_t num, char *buf, size_t bufsiz, int flags) { char *str; (void) memset(buf, '\0', bufsiz); if (num > 1024) { /* Return empty string in case of out-of-memory. */ if ((str = malloc(bufsiz)) == NULL) return (buf); (void) bytecnt2str(num, str, bufsiz); /* Detect overflow. */ if (strlen(str) == 0) { free(str); return (buf); } /* Emit nothing in case of overflow. */ if (snprintf(buf, bufsiz, "%s(%sB)%s", flags & SPC_BEGIN ? " " : "", str, flags & SPC_END ? " " : "") >= bufsiz) (void) memset(buf, '\0', bufsiz); free(str); } return (buf); } /* * Convert 64-bit number to human readable string. Useful mainly for the * byte lifetime counters. Returns pointer to the user supplied buffer. * Able to convert up to Exabytes. Maximum length of the string produced * is 9 characters (not counting the terminating zero). */ char * bytecnt2str(uint64_t num, char *buf, size_t buflen) { uint64_t n = num; char u; int index = 0; while (n >= 1024) { n /= 1024; index++; } /* The field has all units this function can represent. */ u = " KMGTPE"[index]; if (index == 0) { /* Less than 1K */ if (snprintf(buf, buflen, "%llu ", num) >= buflen) (void) memset(buf, '\0', buflen); } else { /* Otherwise display 2 precision digits. */ if (snprintf(buf, buflen, "%.2f %c", (double)num / (1ULL << index * 10), u) >= buflen) (void) memset(buf, '\0', buflen); } return (buf); } /* * secs2str() wrapper. Zeroes out the input buffer and if the number of * seconds to be converted is more than minute, it will produce readable * string in parentheses, store it in the original buffer and return the * pointer to it. */ char * secs2out(unsigned int secs, char *buf, int bufsiz, int flags) { char *str; (void) memset(buf, '\0', bufsiz); if (secs > 60) { /* Return empty string in case of out-of-memory. */ if ((str = malloc(bufsiz)) == NULL) return (buf); (void) secs2str(secs, str, bufsiz); /* Detect overflow. */ if (strlen(str) == 0) { free(str); return (buf); } /* Emit nothing in case of overflow. */ if (snprintf(buf, bufsiz, "%s(%s)%s", flags & SPC_BEGIN ? " " : "", str, flags & SPC_END ? " " : "") >= bufsiz) (void) memset(buf, '\0', bufsiz); free(str); } return (buf); } /* * Convert number of seconds to human readable string. Useful mainly for * the lifetime counters. Returns pointer to the user supplied buffer. * Able to convert up to days. */ char * secs2str(unsigned int secs, char *buf, int bufsiz) { double val = secs; char *unit = "second"; if (val >= 24*60*60) { val /= 86400; unit = "day"; } else if (val >= 60*60) { val /= 60*60; unit = "hour"; } else if (val >= 60) { val /= 60; unit = "minute"; } /* Emit nothing in case of overflow. */ if (snprintf(buf, bufsiz, "%.2f %s%s", val, unit, val >= 2 ? "s" : "") >= bufsiz) (void) memset(buf, '\0', bufsiz); return (buf); } /* * dump_XXX functions produce ASCII output from various structures. * * Because certain errors need to do this to stderr, dump_XXX functions * take a FILE pointer. * * If an error occured while writing to the specified file, these * functions return -1, zero otherwise. */ int dump_sockaddr(struct sockaddr *sa, uint8_t prefixlen, boolean_t addr_only, FILE *where, boolean_t ignore_nss) { struct sockaddr_in *sin; struct sockaddr_in6 *sin6; char *printable_addr, *protocol; uint8_t *addrptr; /* Add 4 chars to hold '/nnn' for prefixes. */ char storage[INET6_ADDRSTRLEN + 4]; uint16_t port; boolean_t unspec; struct hostent *hp; int getipnode_errno, addrlen; switch (sa->sa_family) { case AF_INET: /* LINTED E_BAD_PTR_CAST_ALIGN */ sin = (struct sockaddr_in *)sa; addrptr = (uint8_t *)&sin->sin_addr; port = sin->sin_port; protocol = "AF_INET"; unspec = (sin->sin_addr.s_addr == 0); addrlen = sizeof (sin->sin_addr); break; case AF_INET6: /* LINTED E_BAD_PTR_CAST_ALIGN */ sin6 = (struct sockaddr_in6 *)sa; addrptr = (uint8_t *)&sin6->sin6_addr; port = sin6->sin6_port; protocol = "AF_INET6"; unspec = IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr); addrlen = sizeof (sin6->sin6_addr); break; default: return (0); } if (inet_ntop(sa->sa_family, addrptr, storage, INET6_ADDRSTRLEN) == NULL) { printable_addr = dgettext(TEXT_DOMAIN, "Invalid IP address."); } else { char prefix[5]; /* "/nnn" with terminator. */ (void) snprintf(prefix, sizeof (prefix), "/%d", prefixlen); printable_addr = storage; if (prefixlen != 0) { (void) strlcat(printable_addr, prefix, sizeof (storage)); } } if (addr_only) { if (fprintf(where, "%s", printable_addr) < 0) return (-1); } else { if (fprintf(where, dgettext(TEXT_DOMAIN, "%s: port %d, %s"), protocol, ntohs(port), printable_addr) < 0) return (-1); if (ignore_nss == B_FALSE) { /* * Do AF_independent reverse hostname lookup here. */ if (unspec) { if (fprintf(where, dgettext(TEXT_DOMAIN, " ")) < 0) return (-1); } else { hp = getipnodebyaddr((char *)addrptr, addrlen, sa->sa_family, &getipnode_errno); if (hp != NULL) { if (fprintf(where, " (%s)", hp->h_name) < 0) return (-1); freehostent(hp); } else { if (fprintf(where, dgettext(TEXT_DOMAIN, " ")) < 0) return (-1); } } } if (fputs(".\n", where) == EOF) return (-1); } return (0); } /* * Dump a key, any salt and bitlen. * The key is made up of a stream of bits. If the algorithm requires a salt * value, this will also be part of the dumped key. The last "saltbits" of the * key string, reading left to right will be the salt value. To make it easier * to see which bits make up the key, the salt value is enclosed in []'s. * This function can also be called when ipseckey(8) -s is run, this "saves" * the SAs, including the key to a file. When this is the case, the []'s are * not printed. * * The implementation allows the kernel to be told about the length of the salt * in whole bytes only. If this changes, this function will need to be updated. */ int dump_key(uint8_t *keyp, uint_t bitlen, uint_t saltbits, FILE *where, boolean_t separate_salt) { int numbytes, saltbytes; numbytes = SADB_1TO8(bitlen); saltbytes = SADB_1TO8(saltbits); numbytes += saltbytes; /* The & 0x7 is to check for leftover bits. */ if ((bitlen & 0x7) != 0) numbytes++; while (numbytes-- != 0) { if (pflag) { /* Print no keys if paranoid */ if (fprintf(where, "XX") < 0) return (-1); } else { if (fprintf(where, "%02x", *keyp++) < 0) return (-1); } if (separate_salt && saltbytes != 0 && numbytes == saltbytes) { if (fprintf(where, "[") < 0) return (-1); } } if (separate_salt && saltbits != 0) { if (fprintf(where, "]/%u+%u", bitlen, saltbits) < 0) return (-1); } else { if (fprintf(where, "/%u", bitlen + saltbits) < 0) return (-1); } return (0); } int dump_keystr(uint8_t *keystr, uint_t bitlen, FILE *where) { size_t keylen; uint_t i; /* There should be no leftover bits for a key string */ if ((bitlen & 0x7) != 0) return (-1); keylen = SADB_1TO8(bitlen); for (i = 0; i < keylen; i++) { if (pflag) (void) fprintf(where, "XX"); else if (isprint(keystr[i])) (void) fprintf(where, "%c", keystr[i]); else (void) fprintf(where, "\\x%x", keystr[i]); } return (0); } static struct tcpsigalg { uint8_t ta_value; const char *ta_name; } tcpalgs[] = { { .ta_name = "md5", .ta_value = SADB_AALG_MD5 } }; uint8_t gettcpsigalgbyname(const char *name) { uint_t i; for (i = 0; i < ARRAY_SIZE(tcpalgs); i++) { if (strcmp(name, tcpalgs[i].ta_name) == 0) return (tcpalgs[i].ta_value); } return (0); } const char * gettcpsigalgbynum(uint8_t num) { uint_t i; for (i = 0; i < ARRAY_SIZE(tcpalgs); i++) { if (num == tcpalgs[i].ta_value) return (tcpalgs[i].ta_name); } return (NULL); } /* * Print an authentication or encryption algorithm */ static int dump_generic_alg(uint8_t alg_num, int proto_num, FILE *where) { struct ipsecalgent *alg; alg = getipsecalgbynum(alg_num, proto_num, NULL); if (alg == NULL) { if (fprintf(where, dgettext(TEXT_DOMAIN, ""), alg_num) < 0) return (-1); return (0); } /* * Special-case for backward output compat. * Assume that SADB_AALG_NONE == SADB_EALG_NONE. */ if (alg_num == SADB_AALG_NONE) { if (fputs(dgettext(TEXT_DOMAIN, ""), where) == EOF) return (-1); } else { if (fputs(alg->a_names[0], where) == EOF) return (-1); } freeipsecalgent(alg); return (0); } int dump_aalg(uint8_t aalg, FILE *where) { return (dump_generic_alg(aalg, IPSEC_PROTO_AH, where)); } int dump_ealg(uint8_t ealg, FILE *where) { return (dump_generic_alg(ealg, IPSEC_PROTO_ESP, where)); } int dump_tcpsigalg(uint8_t aalg, FILE *where) { const char *name = gettcpsigalgbynum(aalg); if (name == NULL) { if (fprintf(where, dgettext(TEXT_DOMAIN, ""), aalg) < 0) { return (-1); } return (0); } if (fputs(name, where) == EOF) return (-1); return (0); } /* * Print an SADB_IDENTTYPE string * * Also return TRUE if the actual ident may be printed, FALSE if not. * * If rc is not NULL, set its value to -1 if an error occured while writing * to the specified file, zero otherwise. */ boolean_t dump_sadb_idtype(uint8_t idtype, FILE *where, int *rc) { boolean_t canprint = B_TRUE; int rc_val = 0; switch (idtype) { case SADB_IDENTTYPE_PREFIX: if (fputs(dgettext(TEXT_DOMAIN, "prefix"), where) == EOF) rc_val = -1; break; case SADB_IDENTTYPE_FQDN: if (fputs(dgettext(TEXT_DOMAIN, "FQDN"), where) == EOF) rc_val = -1; break; case SADB_IDENTTYPE_USER_FQDN: if (fputs(dgettext(TEXT_DOMAIN, "user-FQDN (mbox)"), where) == EOF) rc_val = -1; break; case SADB_X_IDENTTYPE_DN: if (fputs(dgettext(TEXT_DOMAIN, "ASN.1 DER Distinguished Name"), where) == EOF) rc_val = -1; canprint = B_FALSE; break; case SADB_X_IDENTTYPE_GN: if (fputs(dgettext(TEXT_DOMAIN, "ASN.1 DER Generic Name"), where) == EOF) rc_val = -1; canprint = B_FALSE; break; case SADB_X_IDENTTYPE_KEY_ID: if (fputs(dgettext(TEXT_DOMAIN, "Generic key id"), where) == EOF) rc_val = -1; break; case SADB_X_IDENTTYPE_ADDR_RANGE: if (fputs(dgettext(TEXT_DOMAIN, "Address range"), where) == EOF) rc_val = -1; break; default: if (fprintf(where, dgettext(TEXT_DOMAIN, ""), idtype) < 0) rc_val = -1; break; } if (rc != NULL) *rc = rc_val; return (canprint); } /* * Slice an argv/argc vector from an interactive line or a read-file line. */ static int create_argv(char *ibuf, int *newargc, char ***thisargv) { unsigned int argvlen = START_ARG; char **current; boolean_t firstchar = B_TRUE; boolean_t inquotes = B_FALSE; *thisargv = malloc(sizeof (char *) * argvlen); if ((*thisargv) == NULL) return (MEMORY_ALLOCATION); current = *thisargv; *current = NULL; for (; *ibuf != '\0'; ibuf++) { if (isspace(*ibuf)) { if (inquotes) { continue; } if (*current != NULL) { *ibuf = '\0'; current++; if (*thisargv + argvlen == current) { /* Regrow ***thisargv. */ if (argvlen == TOO_MANY_ARGS) { free(*thisargv); return (TOO_MANY_TOKENS); } /* Double the allocation. */ current = realloc(*thisargv, sizeof (char *) * (argvlen << 1)); if (current == NULL) { free(*thisargv); return (MEMORY_ALLOCATION); } *thisargv = current; current += argvlen; argvlen <<= 1; /* Double the size. */ } *current = NULL; } } else { if (firstchar) { firstchar = B_FALSE; if (*ibuf == COMMENT_CHAR || *ibuf == '\n') { free(*thisargv); return (COMMENT_LINE); } } if (*ibuf == QUOTE_CHAR) { if (inquotes) { inquotes = B_FALSE; *ibuf = '\0'; } else { inquotes = B_TRUE; } continue; } if (*current == NULL) { *current = ibuf; (*newargc)++; } } } /* * Tricky corner case... * I've parsed _exactly_ the amount of args as I have space. It * won't return NULL-terminated, and bad things will happen to * the caller. */ if (argvlen == *newargc) { current = realloc(*thisargv, sizeof (char *) * (argvlen + 1)); if (current == NULL) { free(*thisargv); return (MEMORY_ALLOCATION); } *thisargv = current; current[argvlen] = NULL; } return (SUCCESS); } /* * init interactive mode if needed and not yet initialized */ static void init_interactive(FILE *infile, CplMatchFn *match_fn) { if (infile == stdin) { if (gl == NULL) { if ((gl = new_GetLine(MAX_LINE_LEN, MAX_CMD_HIST)) == NULL) errx(1, dgettext(TEXT_DOMAIN, "tecla initialization failed")); if (gl_customize_completion(gl, NULL, match_fn) != 0) { (void) del_GetLine(gl); errx(1, dgettext(TEXT_DOMAIN, "tab completion failed to initialize")); } /* * In interactive mode we only want to terminate * when explicitly requested (e.g. by a command). */ (void) sigset(SIGINT, SIG_IGN); } } else { readfile = B_TRUE; } } /* * free tecla data structure */ static void fini_interactive(void) { if (gl != NULL) (void) del_GetLine(gl); } /* * Get single input line, wrapping around interactive and non-interactive * mode. */ static char * do_getstr(FILE *infile, char *prompt, char *ibuf, size_t ibuf_size) { char *line; if (infile != stdin) return (fgets(ibuf, ibuf_size, infile)); /* * If the user hits ^C then we want to catch it and * start over. If the user hits EOF then we want to * bail out. */ once_again: line = gl_get_line(gl, prompt, NULL, -1); if (gl_return_status(gl) == GLR_SIGNAL) { gl_abandon_line(gl); goto once_again; } else if (gl_return_status(gl) == GLR_ERROR) { gl_abandon_line(gl); errx(1, dgettext(TEXT_DOMAIN, "Error reading terminal: %s\n"), gl_error_message(gl, NULL, 0)); } else { if (line != NULL) { if (strlcpy(ibuf, line, ibuf_size) >= ibuf_size) warnx(dgettext(TEXT_DOMAIN, "Line too long (max=%zu chars)"), ibuf_size); line = ibuf; } } return (line); } /* * Enter a mode where commands are read from a file. Treat stdin special. */ void do_interactive(FILE *infile, char *configfile, char *promptstring, char *my_fmri, parse_cmdln_fn parseit, CplMatchFn *match_fn) { char ibuf[IBUF_SIZE], holder[IBUF_SIZE]; char *volatile hptr, **thisargv, *ebuf; int thisargc; volatile boolean_t continue_in_progress = B_FALSE; char *s; (void) setjmp(env); ebuf = NULL; interactive = B_TRUE; bzero(ibuf, IBUF_SIZE); /* panics for us */ init_interactive(infile, match_fn); while ((s = do_getstr(infile, promptstring, ibuf, IBUF_SIZE)) != NULL) { if (readfile) lineno++; thisargc = 0; thisargv = NULL; /* * Check byte IBUF_SIZE - 2, because byte IBUF_SIZE - 1 will * be null-terminated because of fgets(). */ if (ibuf[IBUF_SIZE - 2] != '\0') { if (infile == stdin) { /* do_getstr() issued a warning already */ bzero(ibuf, IBUF_SIZE); continue; } else { ipsecutil_exit(SERVICE_FATAL, my_fmri, debugfile, dgettext(TEXT_DOMAIN, "Line %d too big."), lineno); } } if (!continue_in_progress) { /* Use -2 because of \n from fgets. */ if (ibuf[strlen(ibuf) - 2] == CONT_CHAR) { /* * Can use strcpy here, I've checked the * length already. */ (void) strcpy(holder, ibuf); hptr = &(holder[strlen(holder)]); /* Remove the CONT_CHAR from the string. */ hptr[-2] = ' '; continue_in_progress = B_TRUE; bzero(ibuf, IBUF_SIZE); continue; } } else { /* Handle continuations... */ (void) strncpy(hptr, ibuf, (size_t)(&(holder[IBUF_SIZE]) - hptr)); if (holder[IBUF_SIZE - 1] != '\0') { ipsecutil_exit(SERVICE_FATAL, my_fmri, debugfile, dgettext(TEXT_DOMAIN, "Command buffer overrun.")); } /* Use - 2 because of \n from fgets. */ if (hptr[strlen(hptr) - 2] == CONT_CHAR) { bzero(ibuf, IBUF_SIZE); hptr += strlen(hptr); /* Remove the CONT_CHAR from the string. */ hptr[-2] = ' '; continue; } else { continue_in_progress = B_FALSE; /* * I've already checked the length... */ (void) strcpy(ibuf, holder); } } /* * Just in case the command fails keep a copy of the * command buffer for diagnostic output. */ if (readfile) { /* * The error buffer needs to be big enough to * hold the longest command string, plus * some extra text, see below. */ ebuf = calloc((IBUF_SIZE * 2), sizeof (char)); if (ebuf == NULL) { ipsecutil_exit(SERVICE_FATAL, my_fmri, debugfile, dgettext(TEXT_DOMAIN, "Memory allocation error.")); } else { (void) snprintf(ebuf, (IBUF_SIZE * 2), dgettext(TEXT_DOMAIN, "Config file entry near line %u " "caused error(s) or warnings:\n\n%s\n\n"), lineno, ibuf); } } switch (create_argv(ibuf, &thisargc, &thisargv)) { case TOO_MANY_TOKENS: ipsecutil_exit(SERVICE_BADCONF, my_fmri, debugfile, dgettext(TEXT_DOMAIN, "Too many input tokens.")); break; case MEMORY_ALLOCATION: ipsecutil_exit(SERVICE_BADCONF, my_fmri, debugfile, dgettext(TEXT_DOMAIN, "Memory allocation error.")); break; case COMMENT_LINE: /* Comment line. */ free(ebuf); break; default: if (thisargc != 0) { lines_parsed++; /* ebuf consumed */ parseit(thisargc, thisargv, ebuf, readfile); } else { free(ebuf); } free(thisargv); if (infile == stdin) { (void) printf("%s", promptstring); (void) fflush(stdout); } break; } bzero(ibuf, IBUF_SIZE); } /* * The following code is ipseckey specific. This should never be * used by ikeadm which also calls this function because ikeadm * only runs interactively. If this ever changes this code block * sould be revisited. */ if (readfile) { if (lines_parsed != 0 && lines_added == 0) { ipsecutil_exit(SERVICE_BADCONF, my_fmri, debugfile, dgettext(TEXT_DOMAIN, "Configuration file did not " "contain any valid SAs")); } /* * There were errors. Putting the service in maintenance mode. * When svc.startd(8) allows services to degrade themselves, * this should be revisited. * * If this function was called from a program running as a * smf_method(7), print a warning message. Don't spew out the * errors as these will end up in the smf(7) log file which is * publically readable, the errors may contain sensitive * information. */ if ((lines_added < lines_parsed) && (configfile != NULL)) { if (my_fmri != NULL) { ipsecutil_exit(SERVICE_BADCONF, my_fmri, debugfile, dgettext(TEXT_DOMAIN, "The configuration file contained %d " "errors.\n" "Manually check the configuration with:\n" "ipseckey -c %s\n" "Use svcadm(8) to clear maintenance " "condition when errors are resolved.\n"), lines_parsed - lines_added, configfile); } else { EXIT_BADCONFIG(NULL); } } else { if (my_fmri != NULL) ipsecutil_exit(SERVICE_EXIT_OK, my_fmri, debugfile, dgettext(TEXT_DOMAIN, "%d actions successfully processed."), lines_added); } } else { /* no newline upon Ctrl-D */ if (s != NULL) (void) putchar('\n'); (void) fflush(stdout); } fini_interactive(); EXIT_OK(NULL); } /* * Functions to parse strings that represent a debug or privilege level. * These functions are copied from main.c and door.c in usr.lib/in.iked/common. * If this file evolves into a common library that may be used by in.iked * as well as the usr.sbin utilities, those duplicate functions should be * deleted. * * A privilege level may be represented by a simple keyword, corresponding * to one of the possible levels. A debug level may be represented by a * series of keywords, separated by '+' or '-', indicating categories to * be added or removed from the set of categories in the debug level. * For example, +all-op corresponds to level 0xfffffffb (all flags except * for D_OP set); while p1+p2+pfkey corresponds to level 0x38. Note that * the leading '+' is implicit; the first keyword in the list must be for * a category that is to be added. * * These parsing functions make use of a local version of strtok, strtok_d, * which includes an additional parameter, char *delim. This param is filled * in with the character which ends the returned token. In other words, * this version of strtok, in addition to returning the token, also returns * the single character delimiter from the original string which marked the * end of the token. */ static char * strtok_d(char *string, const char *sepset, char *delim) { static char *lasts; char *q, *r; /* first or subsequent call */ if (string == NULL) string = lasts; if (string == 0) /* return if no tokens remaining */ return (NULL); q = string + strspn(string, sepset); /* skip leading separators */ if (*q == '\0') /* return if no tokens remaining */ return (NULL); if ((r = strpbrk(q, sepset)) == NULL) { /* move past token */ lasts = 0; /* indicate that this is last token */ } else { *delim = *r; /* save delimitor */ *r = '\0'; lasts = r + 1; } return (q); } static keywdtab_t privtab[] = { { IKE_PRIV_MINIMUM, "base" }, { IKE_PRIV_MODKEYS, "modkeys" }, { IKE_PRIV_KEYMAT, "keymat" }, { IKE_PRIV_MINIMUM, "0" }, }; int privstr2num(char *str) { keywdtab_t *pp; char *endp; int priv; for (pp = privtab; pp < A_END(privtab); pp++) { if (strcasecmp(str, pp->kw_str) == 0) return (pp->kw_tag); } priv = strtol(str, &endp, 0); if (*endp == '\0') return (priv); return (-1); } static keywdtab_t dbgtab[] = { { D_CERT, "cert" }, { D_KEY, "key" }, { D_OP, "op" }, { D_P1, "p1" }, { D_P1, "phase1" }, { D_P2, "p2" }, { D_P2, "phase2" }, { D_PFKEY, "pfkey" }, { D_POL, "pol" }, { D_POL, "policy" }, { D_PROP, "prop" }, { D_DOOR, "door" }, { D_CONFIG, "config" }, { D_LABEL, "label" }, { D_ALL, "all" }, { 0, "0" }, }; int dbgstr2num(char *str) { keywdtab_t *dp; for (dp = dbgtab; dp < A_END(dbgtab); dp++) { if (strcasecmp(str, dp->kw_str) == 0) return (dp->kw_tag); } return (D_INVALID); } int parsedbgopts(char *optarg) { char *argp, *endp, op, nextop; int mask = 0, new; mask = strtol(optarg, &endp, 0); if (*endp == '\0') return (mask); op = optarg[0]; if (op != '-') op = '+'; argp = strtok_d(optarg, "+-", &nextop); do { new = dbgstr2num(argp); if (new == D_INVALID) { /* we encountered an invalid keywd */ return (new); } if (op == '+') { mask |= new; } else { mask &= ~new; } op = nextop; } while ((argp = strtok_d(NULL, "+-", &nextop)) != NULL); return (mask); } /* * functions to manipulate the kmcookie-label mapping file */ /* * Open, lockf, fdopen the given file, returning a FILE * on success, * or NULL on failure. */ FILE * kmc_open_and_lock(char *name) { int fd, rtnerr; FILE *fp; if ((fd = open(name, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)) < 0) { return (NULL); } if (lockf(fd, F_LOCK, 0) < 0) { return (NULL); } if ((fp = fdopen(fd, "a+")) == NULL) { return (NULL); } if (fseek(fp, 0, SEEK_SET) < 0) { /* save errno in case fclose changes it */ rtnerr = errno; (void) fclose(fp); errno = rtnerr; return (NULL); } return (fp); } /* * Extract an integer cookie and string label from a line from the * kmcookie-label file. Return -1 on failure, 0 on success. */ int kmc_parse_line(char *line, int *cookie, char **label) { char *cookiestr; *cookie = 0; *label = NULL; cookiestr = strtok(line, " \t\n"); if (cookiestr == NULL) { return (-1); } /* Everything that follows, up to the newline, is the label. */ *label = strtok(NULL, "\n"); if (*label == NULL) { return (-1); } *cookie = atoi(cookiestr); return (0); } /* * Insert a mapping into the file (if it's not already there), given the * new label. Return the assigned cookie, or -1 on error. */ int kmc_insert_mapping(char *label) { FILE *map; char linebuf[IBUF_SIZE]; char *cur_label; int max_cookie = 0, cur_cookie, rtn_cookie; int rtnerr = 0; boolean_t found = B_FALSE; /* open and lock the file; will sleep until lock is available */ if ((map = kmc_open_and_lock(KMCFILE)) == NULL) { /* kmc_open_and_lock() sets errno appropriately */ return (-1); } while (fgets(linebuf, sizeof (linebuf), map) != NULL) { /* Skip blank lines, which often come near EOF. */ if (strlen(linebuf) == 0) continue; if (kmc_parse_line(linebuf, &cur_cookie, &cur_label) < 0) { rtnerr = EINVAL; goto error; } if (cur_cookie > max_cookie) max_cookie = cur_cookie; if ((!found) && (strcmp(cur_label, label) == 0)) { found = B_TRUE; rtn_cookie = cur_cookie; } } if (!found) { rtn_cookie = ++max_cookie; if ((fprintf(map, "%u\t%s\n", rtn_cookie, label) < 0) || (fflush(map) < 0)) { rtnerr = errno; goto error; } } (void) fclose(map); return (rtn_cookie); error: (void) fclose(map); errno = rtnerr; return (-1); } /* * Lookup the given cookie and return its corresponding label. Return * a pointer to the label on success, NULL on error (or if the label is * not found). Note that the returned label pointer points to a static * string, so the label will be overwritten by a subsequent call to the * function; the function is also not thread-safe as a result. * * Because this is possibly publically exported, do not change its name, * but this is for all intents and purposes an IKEv1/in.iked function. */ char * kmc_lookup_by_cookie(int cookie) { FILE *map; static char linebuf[IBUF_SIZE]; char *cur_label; int cur_cookie; if ((map = kmc_open_and_lock(KMCFILE)) == NULL) { return (NULL); } while (fgets(linebuf, sizeof (linebuf), map) != NULL) { if (kmc_parse_line(linebuf, &cur_cookie, &cur_label) < 0) { (void) fclose(map); return (NULL); } if (cookie == cur_cookie) { (void) fclose(map); return (cur_label); } } (void) fclose(map); return (NULL); } /* * Parse basic extension headers and return in the passed-in pointer vector. * Return values include: * * KGE_OK Everything's nice and parsed out. * If there are no extensions, place NULL in extv[0]. * KGE_DUP There is a duplicate extension. * First instance in appropriate bin. First duplicate in * extv[0]. * KGE_UNK Unknown extension type encountered. extv[0] contains * unknown header. * KGE_LEN Extension length error. * KGE_CHK High-level reality check failed on specific extension. * * My apologies for some of the pointer arithmetic in here. I'm thinking * like an assembly programmer, yet trying to make the compiler happy. */ int spdsock_get_ext(spd_ext_t *extv[], spd_msg_t *basehdr, uint_t msgsize, char *diag_buf, uint_t diag_buf_len) { int i; if (diag_buf != NULL) diag_buf[0] = '\0'; for (i = 1; i <= SPD_EXT_MAX; i++) extv[i] = NULL; i = 0; /* Use extv[0] as the "current working pointer". */ extv[0] = (spd_ext_t *)(basehdr + 1); msgsize = SPD_64TO8(msgsize); while ((char *)extv[0] < ((char *)basehdr + msgsize)) { /* Check for unknown headers. */ i++; if (extv[0]->spd_ext_type == 0 || extv[0]->spd_ext_type > SPD_EXT_MAX) { if (diag_buf != NULL) { (void) snprintf(diag_buf, diag_buf_len, "spdsock ext 0x%X unknown: 0x%X", i, extv[0]->spd_ext_type); } return (KGE_UNK); } /* * Check length. Use uint64_t because extlen is in units * of 64-bit words. If length goes beyond the msgsize, * return an error. (Zero length also qualifies here.) */ if (extv[0]->spd_ext_len == 0 || (uint8_t *)((uint64_t *)extv[0] + extv[0]->spd_ext_len) > (uint8_t *)((uint8_t *)basehdr + msgsize)) return (KGE_LEN); /* Check for redundant headers. */ if (extv[extv[0]->spd_ext_type] != NULL) return (KGE_DUP); /* If I make it here, assign the appropriate bin. */ extv[extv[0]->spd_ext_type] = extv[0]; /* Advance pointer (See above for uint64_t ptr reasoning.) */ extv[0] = (spd_ext_t *) ((uint64_t *)extv[0] + extv[0]->spd_ext_len); } /* Everything's cool. */ /* * If extv[0] == NULL, then there are no extension headers in this * message. Ensure that this is the case. */ if (extv[0] == (spd_ext_t *)(basehdr + 1)) extv[0] = NULL; return (KGE_OK); } const char * spdsock_diag(int diagnostic) { switch (diagnostic) { case SPD_DIAGNOSTIC_NONE: return (dgettext(TEXT_DOMAIN, "no error")); case SPD_DIAGNOSTIC_UNKNOWN_EXT: return (dgettext(TEXT_DOMAIN, "unknown extension")); case SPD_DIAGNOSTIC_BAD_EXTLEN: return (dgettext(TEXT_DOMAIN, "bad extension length")); case SPD_DIAGNOSTIC_NO_RULE_EXT: return (dgettext(TEXT_DOMAIN, "no rule extension")); case SPD_DIAGNOSTIC_BAD_ADDR_LEN: return (dgettext(TEXT_DOMAIN, "bad address len")); case SPD_DIAGNOSTIC_MIXED_AF: return (dgettext(TEXT_DOMAIN, "mixed address family")); case SPD_DIAGNOSTIC_ADD_NO_MEM: return (dgettext(TEXT_DOMAIN, "add: no memory")); case SPD_DIAGNOSTIC_ADD_WRONG_ACT_COUNT: return (dgettext(TEXT_DOMAIN, "add: wrong action count")); case SPD_DIAGNOSTIC_ADD_BAD_TYPE: return (dgettext(TEXT_DOMAIN, "add: bad type")); case SPD_DIAGNOSTIC_ADD_BAD_FLAGS: return (dgettext(TEXT_DOMAIN, "add: bad flags")); case SPD_DIAGNOSTIC_ADD_INCON_FLAGS: return (dgettext(TEXT_DOMAIN, "add: inconsistent flags")); case SPD_DIAGNOSTIC_MALFORMED_LCLPORT: return (dgettext(TEXT_DOMAIN, "malformed local port")); case SPD_DIAGNOSTIC_DUPLICATE_LCLPORT: return (dgettext(TEXT_DOMAIN, "duplicate local port")); case SPD_DIAGNOSTIC_MALFORMED_REMPORT: return (dgettext(TEXT_DOMAIN, "malformed remote port")); case SPD_DIAGNOSTIC_DUPLICATE_REMPORT: return (dgettext(TEXT_DOMAIN, "duplicate remote port")); case SPD_DIAGNOSTIC_MALFORMED_PROTO: return (dgettext(TEXT_DOMAIN, "malformed proto")); case SPD_DIAGNOSTIC_DUPLICATE_PROTO: return (dgettext(TEXT_DOMAIN, "duplicate proto")); case SPD_DIAGNOSTIC_MALFORMED_LCLADDR: return (dgettext(TEXT_DOMAIN, "malformed local address")); case SPD_DIAGNOSTIC_DUPLICATE_LCLADDR: return (dgettext(TEXT_DOMAIN, "duplicate local address")); case SPD_DIAGNOSTIC_MALFORMED_REMADDR: return (dgettext(TEXT_DOMAIN, "malformed remote address")); case SPD_DIAGNOSTIC_DUPLICATE_REMADDR: return (dgettext(TEXT_DOMAIN, "duplicate remote address")); case SPD_DIAGNOSTIC_MALFORMED_ACTION: return (dgettext(TEXT_DOMAIN, "malformed action")); case SPD_DIAGNOSTIC_DUPLICATE_ACTION: return (dgettext(TEXT_DOMAIN, "duplicate action")); case SPD_DIAGNOSTIC_MALFORMED_RULE: return (dgettext(TEXT_DOMAIN, "malformed rule")); case SPD_DIAGNOSTIC_DUPLICATE_RULE: return (dgettext(TEXT_DOMAIN, "duplicate rule")); case SPD_DIAGNOSTIC_MALFORMED_RULESET: return (dgettext(TEXT_DOMAIN, "malformed ruleset")); case SPD_DIAGNOSTIC_DUPLICATE_RULESET: return (dgettext(TEXT_DOMAIN, "duplicate ruleset")); case SPD_DIAGNOSTIC_INVALID_RULE_INDEX: return (dgettext(TEXT_DOMAIN, "invalid rule index")); case SPD_DIAGNOSTIC_BAD_SPDID: return (dgettext(TEXT_DOMAIN, "bad spdid")); case SPD_DIAGNOSTIC_BAD_MSG_TYPE: return (dgettext(TEXT_DOMAIN, "bad message type")); case SPD_DIAGNOSTIC_UNSUPP_AH_ALG: return (dgettext(TEXT_DOMAIN, "unsupported AH algorithm")); case SPD_DIAGNOSTIC_UNSUPP_ESP_ENCR_ALG: return (dgettext(TEXT_DOMAIN, "unsupported ESP encryption algorithm")); case SPD_DIAGNOSTIC_UNSUPP_ESP_AUTH_ALG: return (dgettext(TEXT_DOMAIN, "unsupported ESP authentication algorithm")); case SPD_DIAGNOSTIC_UNSUPP_AH_KEYSIZE: return (dgettext(TEXT_DOMAIN, "unsupported AH key size")); case SPD_DIAGNOSTIC_UNSUPP_ESP_ENCR_KEYSIZE: return (dgettext(TEXT_DOMAIN, "unsupported ESP encryption key size")); case SPD_DIAGNOSTIC_UNSUPP_ESP_AUTH_KEYSIZE: return (dgettext(TEXT_DOMAIN, "unsupported ESP authentication key size")); case SPD_DIAGNOSTIC_NO_ACTION_EXT: return (dgettext(TEXT_DOMAIN, "No ACTION extension")); case SPD_DIAGNOSTIC_ALG_ID_RANGE: return (dgettext(TEXT_DOMAIN, "invalid algorithm identifer")); case SPD_DIAGNOSTIC_ALG_NUM_KEY_SIZES: return (dgettext(TEXT_DOMAIN, "number of key sizes inconsistent")); case SPD_DIAGNOSTIC_ALG_NUM_BLOCK_SIZES: return (dgettext(TEXT_DOMAIN, "number of block sizes inconsistent")); case SPD_DIAGNOSTIC_ALG_MECH_NAME_LEN: return (dgettext(TEXT_DOMAIN, "invalid mechanism name length")); case SPD_DIAGNOSTIC_NOT_GLOBAL_OP: return (dgettext(TEXT_DOMAIN, "operation not applicable to all policies")); case SPD_DIAGNOSTIC_NO_TUNNEL_SELECTORS: return (dgettext(TEXT_DOMAIN, "using selectors on a transport-mode tunnel")); default: return (dgettext(TEXT_DOMAIN, "unknown diagnostic")); } } /* * PF_KEY Diagnostic table. * * PF_KEY NOTE: If you change pfkeyv2.h's SADB_X_DIAGNOSTIC_* space, this is * where you need to add new messages. */ const char * keysock_diag(int diagnostic) { switch (diagnostic) { case SADB_X_DIAGNOSTIC_NONE: return (dgettext(TEXT_DOMAIN, "No diagnostic")); case SADB_X_DIAGNOSTIC_UNKNOWN_MSG: return (dgettext(TEXT_DOMAIN, "Unknown message type")); case SADB_X_DIAGNOSTIC_UNKNOWN_EXT: return (dgettext(TEXT_DOMAIN, "Unknown extension type")); case SADB_X_DIAGNOSTIC_BAD_EXTLEN: return (dgettext(TEXT_DOMAIN, "Bad extension length")); case SADB_X_DIAGNOSTIC_UNKNOWN_SATYPE: return (dgettext(TEXT_DOMAIN, "Unknown Security Association type")); case SADB_X_DIAGNOSTIC_SATYPE_NEEDED: return (dgettext(TEXT_DOMAIN, "Specific Security Association type needed")); case SADB_X_DIAGNOSTIC_NO_SADBS: return (dgettext(TEXT_DOMAIN, "No Security Association Databases present")); case SADB_X_DIAGNOSTIC_NO_EXT: return (dgettext(TEXT_DOMAIN, "No extensions needed for message")); case SADB_X_DIAGNOSTIC_BAD_SRC_AF: return (dgettext(TEXT_DOMAIN, "Bad source address family")); case SADB_X_DIAGNOSTIC_BAD_DST_AF: return (dgettext(TEXT_DOMAIN, "Bad destination address family")); case SADB_X_DIAGNOSTIC_BAD_PROXY_AF: return (dgettext(TEXT_DOMAIN, "Bad inner-source address family")); case SADB_X_DIAGNOSTIC_AF_MISMATCH: return (dgettext(TEXT_DOMAIN, "Source/destination address family mismatch")); case SADB_X_DIAGNOSTIC_BAD_SRC: return (dgettext(TEXT_DOMAIN, "Bad source address value")); case SADB_X_DIAGNOSTIC_BAD_DST: return (dgettext(TEXT_DOMAIN, "Bad destination address value")); case SADB_X_DIAGNOSTIC_ALLOC_HSERR: return (dgettext(TEXT_DOMAIN, "Soft allocations limit more than hard limit")); case SADB_X_DIAGNOSTIC_BYTES_HSERR: return (dgettext(TEXT_DOMAIN, "Soft bytes limit more than hard limit")); case SADB_X_DIAGNOSTIC_ADDTIME_HSERR: return (dgettext(TEXT_DOMAIN, "Soft add expiration time later " "than hard expiration time")); case SADB_X_DIAGNOSTIC_USETIME_HSERR: return (dgettext(TEXT_DOMAIN, "Soft use expiration time later " "than hard expiration time")); case SADB_X_DIAGNOSTIC_MISSING_SRC: return (dgettext(TEXT_DOMAIN, "Missing source address")); case SADB_X_DIAGNOSTIC_MISSING_DST: return (dgettext(TEXT_DOMAIN, "Missing destination address")); case SADB_X_DIAGNOSTIC_MISSING_SA: return (dgettext(TEXT_DOMAIN, "Missing SA extension")); case SADB_X_DIAGNOSTIC_MISSING_EKEY: return (dgettext(TEXT_DOMAIN, "Missing encryption key")); case SADB_X_DIAGNOSTIC_MISSING_AKEY: return (dgettext(TEXT_DOMAIN, "Missing authentication key")); case SADB_X_DIAGNOSTIC_MISSING_RANGE: return (dgettext(TEXT_DOMAIN, "Missing SPI range")); case SADB_X_DIAGNOSTIC_DUPLICATE_SRC: return (dgettext(TEXT_DOMAIN, "Duplicate source address")); case SADB_X_DIAGNOSTIC_DUPLICATE_DST: return (dgettext(TEXT_DOMAIN, "Duplicate destination address")); case SADB_X_DIAGNOSTIC_DUPLICATE_SA: return (dgettext(TEXT_DOMAIN, "Duplicate SA extension")); case SADB_X_DIAGNOSTIC_DUPLICATE_EKEY: return (dgettext(TEXT_DOMAIN, "Duplicate encryption key")); case SADB_X_DIAGNOSTIC_DUPLICATE_AKEY: return (dgettext(TEXT_DOMAIN, "Duplicate authentication key")); case SADB_X_DIAGNOSTIC_DUPLICATE_RANGE: return (dgettext(TEXT_DOMAIN, "Duplicate SPI range")); case SADB_X_DIAGNOSTIC_MALFORMED_SRC: return (dgettext(TEXT_DOMAIN, "Malformed source address")); case SADB_X_DIAGNOSTIC_MALFORMED_DST: return (dgettext(TEXT_DOMAIN, "Malformed destination address")); case SADB_X_DIAGNOSTIC_MALFORMED_SA: return (dgettext(TEXT_DOMAIN, "Malformed SA extension")); case SADB_X_DIAGNOSTIC_MALFORMED_EKEY: return (dgettext(TEXT_DOMAIN, "Malformed encryption key")); case SADB_X_DIAGNOSTIC_MALFORMED_AKEY: return (dgettext(TEXT_DOMAIN, "Malformed authentication key")); case SADB_X_DIAGNOSTIC_MALFORMED_RANGE: return (dgettext(TEXT_DOMAIN, "Malformed SPI range")); case SADB_X_DIAGNOSTIC_AKEY_PRESENT: return (dgettext(TEXT_DOMAIN, "Authentication key not needed")); case SADB_X_DIAGNOSTIC_EKEY_PRESENT: return (dgettext(TEXT_DOMAIN, "Encryption key not needed")); case SADB_X_DIAGNOSTIC_PROP_PRESENT: return (dgettext(TEXT_DOMAIN, "Proposal extension not needed")); case SADB_X_DIAGNOSTIC_SUPP_PRESENT: return (dgettext(TEXT_DOMAIN, "Supported algorithms extension not needed")); case SADB_X_DIAGNOSTIC_BAD_AALG: return (dgettext(TEXT_DOMAIN, "Unsupported authentication algorithm")); case SADB_X_DIAGNOSTIC_BAD_EALG: return (dgettext(TEXT_DOMAIN, "Unsupported encryption algorithm")); case SADB_X_DIAGNOSTIC_BAD_SAFLAGS: return (dgettext(TEXT_DOMAIN, "Invalid SA flags")); case SADB_X_DIAGNOSTIC_BAD_SASTATE: return (dgettext(TEXT_DOMAIN, "Invalid SA state")); case SADB_X_DIAGNOSTIC_BAD_AKEYBITS: return (dgettext(TEXT_DOMAIN, "Bad number of authentication bits")); case SADB_X_DIAGNOSTIC_BAD_EKEYBITS: return (dgettext(TEXT_DOMAIN, "Bad number of encryption bits")); case SADB_X_DIAGNOSTIC_ENCR_NOTSUPP: return (dgettext(TEXT_DOMAIN, "Encryption not supported for this SA type")); case SADB_X_DIAGNOSTIC_WEAK_EKEY: return (dgettext(TEXT_DOMAIN, "Weak encryption key")); case SADB_X_DIAGNOSTIC_WEAK_AKEY: return (dgettext(TEXT_DOMAIN, "Weak authentication key")); case SADB_X_DIAGNOSTIC_DUPLICATE_KMP: return (dgettext(TEXT_DOMAIN, "Duplicate key management protocol")); case SADB_X_DIAGNOSTIC_DUPLICATE_KMC: return (dgettext(TEXT_DOMAIN, "Duplicate key management cookie")); case SADB_X_DIAGNOSTIC_MISSING_NATT_LOC: return (dgettext(TEXT_DOMAIN, "Missing NAT-T local address")); case SADB_X_DIAGNOSTIC_MISSING_NATT_REM: return (dgettext(TEXT_DOMAIN, "Missing NAT-T remote address")); case SADB_X_DIAGNOSTIC_DUPLICATE_NATT_LOC: return (dgettext(TEXT_DOMAIN, "Duplicate NAT-T local address")); case SADB_X_DIAGNOSTIC_DUPLICATE_NATT_REM: return (dgettext(TEXT_DOMAIN, "Duplicate NAT-T remote address")); case SADB_X_DIAGNOSTIC_MALFORMED_NATT_LOC: return (dgettext(TEXT_DOMAIN, "Malformed NAT-T local address")); case SADB_X_DIAGNOSTIC_MALFORMED_NATT_REM: return (dgettext(TEXT_DOMAIN, "Malformed NAT-T remote address")); case SADB_X_DIAGNOSTIC_DUPLICATE_NATT_PORTS: return (dgettext(TEXT_DOMAIN, "Duplicate NAT-T ports")); case SADB_X_DIAGNOSTIC_MISSING_INNER_SRC: return (dgettext(TEXT_DOMAIN, "Missing inner source address")); case SADB_X_DIAGNOSTIC_MISSING_INNER_DST: return (dgettext(TEXT_DOMAIN, "Missing inner destination address")); case SADB_X_DIAGNOSTIC_DUPLICATE_INNER_SRC: return (dgettext(TEXT_DOMAIN, "Duplicate inner source address")); case SADB_X_DIAGNOSTIC_DUPLICATE_INNER_DST: return (dgettext(TEXT_DOMAIN, "Duplicate inner destination address")); case SADB_X_DIAGNOSTIC_MALFORMED_INNER_SRC: return (dgettext(TEXT_DOMAIN, "Malformed inner source address")); case SADB_X_DIAGNOSTIC_MALFORMED_INNER_DST: return (dgettext(TEXT_DOMAIN, "Malformed inner destination address")); case SADB_X_DIAGNOSTIC_PREFIX_INNER_SRC: return (dgettext(TEXT_DOMAIN, "Invalid inner-source prefix length ")); case SADB_X_DIAGNOSTIC_PREFIX_INNER_DST: return (dgettext(TEXT_DOMAIN, "Invalid inner-destination prefix length")); case SADB_X_DIAGNOSTIC_BAD_INNER_DST_AF: return (dgettext(TEXT_DOMAIN, "Bad inner-destination address family")); case SADB_X_DIAGNOSTIC_INNER_AF_MISMATCH: return (dgettext(TEXT_DOMAIN, "Inner source/destination address family mismatch")); case SADB_X_DIAGNOSTIC_BAD_NATT_REM_AF: return (dgettext(TEXT_DOMAIN, "Bad NAT-T remote address family")); case SADB_X_DIAGNOSTIC_BAD_NATT_LOC_AF: return (dgettext(TEXT_DOMAIN, "Bad NAT-T local address family")); case SADB_X_DIAGNOSTIC_PROTO_MISMATCH: return (dgettext(TEXT_DOMAIN, "Source/desination protocol mismatch")); case SADB_X_DIAGNOSTIC_INNER_PROTO_MISMATCH: return (dgettext(TEXT_DOMAIN, "Inner source/desination protocol mismatch")); case SADB_X_DIAGNOSTIC_DUAL_PORT_SETS: return (dgettext(TEXT_DOMAIN, "Both inner ports and outer ports are set")); case SADB_X_DIAGNOSTIC_PAIR_INAPPROPRIATE: return (dgettext(TEXT_DOMAIN, "Pairing failed, target SA unsuitable for pairing")); case SADB_X_DIAGNOSTIC_PAIR_ADD_MISMATCH: return (dgettext(TEXT_DOMAIN, "Source/destination address differs from pair SA")); case SADB_X_DIAGNOSTIC_PAIR_ALREADY: return (dgettext(TEXT_DOMAIN, "Already paired with another security association")); case SADB_X_DIAGNOSTIC_PAIR_SA_NOTFOUND: return (dgettext(TEXT_DOMAIN, "Command failed, pair security association not found")); case SADB_X_DIAGNOSTIC_BAD_SA_DIRECTION: return (dgettext(TEXT_DOMAIN, "Inappropriate SA direction")); case SADB_X_DIAGNOSTIC_SA_NOTFOUND: return (dgettext(TEXT_DOMAIN, "Security association not found")); case SADB_X_DIAGNOSTIC_SA_EXPIRED: return (dgettext(TEXT_DOMAIN, "Security association is not valid")); case SADB_X_DIAGNOSTIC_BAD_CTX: return (dgettext(TEXT_DOMAIN, "Algorithm invalid or not supported by Crypto Framework")); case SADB_X_DIAGNOSTIC_INVALID_REPLAY: return (dgettext(TEXT_DOMAIN, "Invalid Replay counter")); case SADB_X_DIAGNOSTIC_MISSING_LIFETIME: return (dgettext(TEXT_DOMAIN, "Inappropriate lifetimes")); case SADB_X_DIAGNOSTIC_MISSING_ASTR: return (dgettext(TEXT_DOMAIN, "Missing authentication string")); case SADB_X_DIAGNOSTIC_DUPLICATE_ASTR: return (dgettext(TEXT_DOMAIN, "Duplicate authentication string")); case SADB_X_DIAGNOSTIC_MALFORMED_ASTR: return (dgettext(TEXT_DOMAIN, "Malformed authentication string")); default: return (dgettext(TEXT_DOMAIN, "Unknown diagnostic code")); } } /* * Convert an IPv6 mask to a prefix len. I assume all IPv6 masks are * contiguous, so I stop at the first zero bit! */ int in_masktoprefix(uint8_t *mask, boolean_t is_v4mapped) { int rc = 0; uint8_t last; int limit = IPV6_ABITS; if (is_v4mapped) { mask += ((IPV6_ABITS - IP_ABITS)/8); limit = IP_ABITS; } while (*mask == 0xff) { rc += 8; if (rc == limit) return (limit); mask++; } last = *mask; while (last != 0) { rc++; last = (last << 1) & 0xff; } return (rc); } /* * Expand the diagnostic code into a message. */ void print_diagnostic(FILE *file, uint16_t diagnostic) { /* Use two spaces so above strings can fit on the line. */ (void) fprintf(file, dgettext(TEXT_DOMAIN, " Diagnostic code %u: %s.\n"), diagnostic, keysock_diag(diagnostic)); } /* * Prints the base PF_KEY message. */ void print_sadb_msg(FILE *file, struct sadb_msg *samsg, time_t wallclock, boolean_t vflag) { if (wallclock != 0) printsatime(file, wallclock, dgettext(TEXT_DOMAIN, "%sTimestamp: %s\n"), "", NULL, vflag); (void) fprintf(file, dgettext(TEXT_DOMAIN, "Base message (version %u) type "), samsg->sadb_msg_version); switch (samsg->sadb_msg_type) { case SADB_RESERVED: (void) fprintf(file, dgettext(TEXT_DOMAIN, "RESERVED (warning: set to 0)")); break; case SADB_GETSPI: (void) fprintf(file, "GETSPI"); break; case SADB_UPDATE: (void) fprintf(file, "UPDATE"); break; case SADB_X_UPDATEPAIR: (void) fprintf(file, "UPDATE PAIR"); break; case SADB_ADD: (void) fprintf(file, "ADD"); break; case SADB_DELETE: (void) fprintf(file, "DELETE"); break; case SADB_X_DELPAIR: (void) fprintf(file, "DELETE PAIR"); break; case SADB_GET: (void) fprintf(file, "GET"); break; case SADB_ACQUIRE: (void) fprintf(file, "ACQUIRE"); break; case SADB_REGISTER: (void) fprintf(file, "REGISTER"); break; case SADB_EXPIRE: (void) fprintf(file, "EXPIRE"); break; case SADB_FLUSH: (void) fprintf(file, "FLUSH"); break; case SADB_DUMP: (void) fprintf(file, "DUMP"); break; case SADB_X_PROMISC: (void) fprintf(file, "X_PROMISC"); break; case SADB_X_INVERSE_ACQUIRE: (void) fprintf(file, "X_INVERSE_ACQUIRE"); break; default: (void) fprintf(file, dgettext(TEXT_DOMAIN, "Unknown (%u)"), samsg->sadb_msg_type); break; } (void) fprintf(file, dgettext(TEXT_DOMAIN, ", SA type ")); switch (samsg->sadb_msg_satype) { case SADB_SATYPE_UNSPEC: (void) fprintf(file, dgettext(TEXT_DOMAIN, "")); break; case SADB_SATYPE_AH: (void) fprintf(file, "AH"); break; case SADB_SATYPE_ESP: (void) fprintf(file, "ESP"); break; case SADB_X_SATYPE_TCPSIG: (void) fprintf(file, "TCPSIG"); break; case SADB_SATYPE_RSVP: (void) fprintf(file, "RSVP"); break; case SADB_SATYPE_OSPFV2: (void) fprintf(file, "OSPFv2"); break; case SADB_SATYPE_RIPV2: (void) fprintf(file, "RIPv2"); break; case SADB_SATYPE_MIP: (void) fprintf(file, dgettext(TEXT_DOMAIN, "Mobile IP")); break; default: (void) fprintf(file, dgettext(TEXT_DOMAIN, ""), samsg->sadb_msg_satype); break; } (void) fprintf(file, ".\n"); if (samsg->sadb_msg_errno != 0) { (void) fprintf(file, dgettext(TEXT_DOMAIN, "Error %s from PF_KEY.\n"), strerror(samsg->sadb_msg_errno)); print_diagnostic(file, samsg->sadb_x_msg_diagnostic); } (void) fprintf(file, dgettext(TEXT_DOMAIN, "Message length %u bytes, seq=%u, pid=%u.\n"), SADB_64TO8(samsg->sadb_msg_len), samsg->sadb_msg_seq, samsg->sadb_msg_pid); } /* * Print the SA extension for PF_KEY. */ void print_sa(FILE *file, char *prefix, struct sadb_sa *assoc) { if (assoc->sadb_sa_len != SADB_8TO64(sizeof (*assoc))) { warnxfp(EFD(file), dgettext(TEXT_DOMAIN, "WARNING: SA info extension length (%u) is bad."), SADB_64TO8(assoc->sadb_sa_len)); } if ((assoc->sadb_sa_flags & SADB_X_SAFLAGS_TCPSIG) == 0) { (void) fprintf(file, dgettext(TEXT_DOMAIN, "%sSADB_ASSOC spi=0x%x, replay window size=%u, state="), prefix, ntohl(assoc->sadb_sa_spi), assoc->sadb_sa_replay); } else { (void) fprintf(file, dgettext(TEXT_DOMAIN, "%sSADB_ASSOC state="), prefix); } switch (assoc->sadb_sa_state) { case SADB_SASTATE_LARVAL: (void) fprintf(file, dgettext(TEXT_DOMAIN, "LARVAL")); break; case SADB_SASTATE_MATURE: (void) fprintf(file, dgettext(TEXT_DOMAIN, "MATURE")); break; case SADB_SASTATE_DYING: (void) fprintf(file, dgettext(TEXT_DOMAIN, "DYING")); break; case SADB_SASTATE_DEAD: (void) fprintf(file, dgettext(TEXT_DOMAIN, "DEAD")); break; case SADB_X_SASTATE_ACTIVE_ELSEWHERE: (void) fprintf(file, dgettext(TEXT_DOMAIN, "ACTIVE_ELSEWHERE")); break; case SADB_X_SASTATE_IDLE: (void) fprintf(file, dgettext(TEXT_DOMAIN, "IDLE")); break; default: (void) fprintf(file, dgettext(TEXT_DOMAIN, ""), assoc->sadb_sa_state); } if (assoc->sadb_sa_auth != SADB_AALG_NONE) { (void) fprintf(file, dgettext(TEXT_DOMAIN, "\n%sAuthentication algorithm = "), prefix); if ((assoc->sadb_sa_flags & SADB_X_SAFLAGS_TCPSIG) != 0) (void) dump_tcpsigalg(assoc->sadb_sa_auth, file); else (void) dump_aalg(assoc->sadb_sa_auth, file); } if (assoc->sadb_sa_encrypt != SADB_EALG_NONE) { (void) fprintf(file, dgettext(TEXT_DOMAIN, "\n%sEncryption algorithm = "), prefix); (void) dump_ealg(assoc->sadb_sa_encrypt, file); } (void) fprintf(file, dgettext(TEXT_DOMAIN, "\n%sflags=0x%x < "), prefix, assoc->sadb_sa_flags); if (assoc->sadb_sa_flags & SADB_SAFLAGS_PFS) (void) fprintf(file, "PFS "); if (assoc->sadb_sa_flags & SADB_SAFLAGS_NOREPLAY) (void) fprintf(file, "NOREPLAY "); /* BEGIN Solaris-specific flags. */ if (assoc->sadb_sa_flags & SADB_X_SAFLAGS_USED) (void) fprintf(file, "X_USED "); if (assoc->sadb_sa_flags & SADB_X_SAFLAGS_PAIRED) (void) fprintf(file, "X_PAIRED "); if (assoc->sadb_sa_flags & SADB_X_SAFLAGS_OUTBOUND) (void) fprintf(file, "X_OUTBOUND "); if (assoc->sadb_sa_flags & SADB_X_SAFLAGS_INBOUND) (void) fprintf(file, "X_INBOUND "); if (assoc->sadb_sa_flags & SADB_X_SAFLAGS_UNIQUE) (void) fprintf(file, "X_UNIQUE "); if (assoc->sadb_sa_flags & SADB_X_SAFLAGS_AALG1) (void) fprintf(file, "X_AALG1 "); if (assoc->sadb_sa_flags & SADB_X_SAFLAGS_AALG2) (void) fprintf(file, "X_AALG2 "); if (assoc->sadb_sa_flags & SADB_X_SAFLAGS_EALG1) (void) fprintf(file, "X_EALG1 "); if (assoc->sadb_sa_flags & SADB_X_SAFLAGS_EALG2) (void) fprintf(file, "X_EALG2 "); if (assoc->sadb_sa_flags & SADB_X_SAFLAGS_NATT_LOC) (void) fprintf(file, "X_NATT_LOC "); if (assoc->sadb_sa_flags & SADB_X_SAFLAGS_NATT_REM) (void) fprintf(file, "X_NATT_REM "); if (assoc->sadb_sa_flags & SADB_X_SAFLAGS_TUNNEL) (void) fprintf(file, "X_TUNNEL "); if (assoc->sadb_sa_flags & SADB_X_SAFLAGS_NATTED) (void) fprintf(file, "X_NATTED "); if (assoc->sadb_sa_flags & SADB_X_SAFLAGS_TCPSIG) (void) fprintf(file, "X_TCPSIG "); /* END Solaris-specific flags. */ (void) fprintf(file, ">\n"); } void printsatime(FILE *file, int64_t lt, const char *msg, const char *pfx, const char *pfx2, boolean_t vflag) { char tbuf[TBUF_SIZE]; /* For strftime() call. */ const char *tp = tbuf; time_t t = lt; struct tm res; if (t != lt) { if (lt > 0) t = LONG_MAX; else t = LONG_MIN; } if (strftime(tbuf, TBUF_SIZE, NULL, localtime_r(&t, &res)) == 0) tp = dgettext(TEXT_DOMAIN, "