ipseckey.c revision d0115d88cdf265fa2cc0481f8a6db735be47f2b9
1N/A * The contents of this file are subject to the terms of the 1N/A * Common Development and Distribution License (the "License"). 1N/A * You may not use this file except in compliance with the License. 1N/A * See the License for the specific language governing permissions 1N/A * and limitations under the License. 1N/A * When distributing Covered Code, include this CDDL HEADER in each 1N/A * If applicable, add the following below this CDDL HEADER, with the 1N/A * fields enclosed by brackets "[]" replaced with your own identifying 1N/A * information: Portions Copyright [yyyy] [name of copyright owner] 1N/A * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 1N/A * Use is subject to license terms. 1N/A * NOTE:I'm trying to use "struct sadb_foo" instead of "sadb_foo_t" 1N/A * as a maximal PF_KEY portability test. 1N/A * Also, this is a deliberately single-threaded app, also for portability 1N/A * to systems without POSIX threads. 1N/A * WARN() and ERROR() do the same thing really, with ERROR() the function 1N/A * that prints the error buffer needs to be called at the end of a code block 1N/A * This will print out all accumulated errors before bailing. The WARN() 1N/A * macro calls handle_errors() in such a way that it prints the message 1N/A * If the FATAL() macro used call handle_errors() immediately. 1N/A/* Defined as a uint64_t array for alignment purposes. */ 1N/A * Disable default TAB completion for now (until some brave soul tackles it). 1N/A * Create/Grow a buffer large enough to hold error messages. If *ebuf 1N/A * is not NULL then it will contain a copy of the command line that 1N/A * append new messages to the existing buffer. 1N/A /* There is a new line character */ Bail(
"realloc() failure");
* If (ep == NULL) then this is the first error to record, * copy in the command line that triggered this error/warning. * If not in interactive mode print usage message and exit. "ipseckey [ -nvp ] | cmd [sa_type] [extfield value]*\n"));
gettext(
"\tipseckey [ -nvp ] -f infile\n"));
gettext(
"\tipseckey [ -nvp ] -s outfile\n"));
gettext(
"Type help or ? for usage info\n"));
* Print out any errors, tidy up as required. * error pointer ep will be free()'d * For now suppress the errors when run from smf(5) * because potentially sensitive information could * end up in a publicly readable logfile. /* reset command buffer */ * No errors, if this is the last time that this function * is called, free(ebuf) and reset command buffer. /* reset command buffer */ * Initialize a PF_KEY base message. * parseXXX and rparseXXX commands parse input and convert them to PF_KEY * field values, or do the reverse for the purposes of saving the SA tables. * (See the save_XXX functions.) * Q: Do we want to do GETSPI? * A: No, it's for automated key mgmt. only. Either that, * or it isn't relevant until we support non IPsec SA types. * Q: And ACQUIRE and REGISTER and EXPIRE? * A: not until we support non IPsec SA types. * Convert a number from a command line. I picked "u_longlong_t" for the * number because we need the largest number available. Also, the strto<num> * calls don't deal in units of uintNN_t. " was expecting a number.\n"));
"Expecting a number, not \"%s\"!\n"),
num);
* -1, while not optimal, is sufficiently out of range * for most of this function's applications when * Parse and reverse parse a specific SA type (AH, ESP, etc.). /* PF_KEY NOTE: More to come if net/pfkeyv2.h gets updated. */ {
NULL, 0}
/* Token value is irrelevant for this entry. */ * New SA types (including ones keysock maintains for user-land * protocols) may be added, so parse a numeric value if possible. "Unknown SA type (%s).\n"),
type);
/* "String", token value, next arg is */ * Q: Do I need stuff for proposals, combinations, supported algorithms, * A: Probably not, but you never know. * Parse out extension header type values. * Since the OS controls what extensions are available, we don't have * to parse numeric values here. * Parse possible state values. "was expecting a state.\n");
* Return the numerical algorithm identifier corresponding to the specified "was expecting an algorithm name.\n"));
"Using manual keying with a Counter mode algorithm " "such as \"%s\" may be insecure!\n"),
* Since algorithms can be loaded during kernel run-time, check for * numeric algorithm values too. PF_KEY can catch bad ones with EINVAL. "Unknown encryption algorithm type \"%s\"\n"),
alg);
"Unknown authentication algorithm type \"%s\"\n"),
alg);
/* Shouldn't reach here, see callers for why. */ "was expecting a type.\n"));
* Since identity types are almost arbitrary, check for numeric * algorithm values too. PF_KEY can catch bad ones with EINVAL. * Parse an address off the command line. Return length of sockaddr, * and either return a hostent pointer (caller frees). The new * getipnodebyname() call does the Right Thing (TM), even with * raw addresses (colon-separated IPv6 or dotted decimal IPv4). "was expecting an address.\n"));
* Try name->address first. Assume AF_INET6, and * get IPv4's, plus IPv6's if and only if IPv6 is configured. * This means to add IPv6 SAs, you must have IPv6 * up-and-running. (AI_DEFAULT works here.) * Try a normal address conversion only. Use "dummy" * to construct a fake hostent. Caller will know not * Remap to AF_INET6 anyway. * NOTE: If macro changes to disallow in-place * conversion, rewhack this. /* Always return sockaddr_in6 for now. */ * Parse a hex character for a key. A string will take the form: * xxxxxxxxx == a string of hex characters ([0-9][a-f][A-F]) * nn == an optional decimal "mask". If it is not present, it * is assumed that the hex string will be rounded to the nearest * byte, where odd nibbles, like 123 will become 0x0123. * NOTE:Unlike the expression of IP addresses, I will not allow an * excessive "mask". For example 2112/50 is very illegal. * NOTE2: This key should be in canonical order. Consult your man * pages per algorithm about said order. (((
hd) >=
'a' && (
hd) <=
'f') ? ((
hd) -
'a' +
10) : ((
hd) -
'A' +
10)))
"was expecting a key.\n"));
/* Allow hex values prepended with 0x convention */ for (i = 0;
input[i] !=
'\0' &&
input[i] !=
'/'; i++)
"\"%s\" is not a bit specifier.\n"),
"bit length %d is too big for %s.\n"),
bits,
input);
* Adjust hexlen down if user gave us too small of a bit "WARNING: Lower bits will be truncated " * Allocate. Remember, hexlen is in nibbles. Bail(
"malloc(parsekey)");
* Read in nibbles. Read in odd-numbered as shifted high. * (e.g. 123 becomes 0x1230). for (i = 0;
input[i] !=
'\0'; i +=
2) {
"string '%s' not a hex value.\n"),
input);
break;
/* out of for loop. */ /* bzero the remaining bits if we're a non-octet amount. */ 0xff << (
8 - (
bits &
0x7));
int doi =
1;
/* XXX XXX DEFAULT_DOI XXX XXX */ Bail(
"malloc parsed label");
/* Should exit before reaching here... */ * Return a different return code for a bad label, but really, * this would be a caller error. * Write a message to the PF_KEY socket. If verbose, print the message * heading into the kernel. gettext(
"VERBOSE ON: Message to kernel looks like:\n"));
(
void)
printf(
"==========================================\n");
(
void)
printf(
"==========================================\n");
* SIGALRM handler for time_critical_enter. errx(
1,
gettext(
"Reply message from PF_KEY timed out."));
errx(
1,
gettext(
"Caught signal %d while trying to receive" "PF_KEY reply message"),
signal);
* Enter a "time critical" section where key is waiting for a return message. * Exit the "time critical" section after getting an appropriate return * Construct a PF_KEY FLUSH message for the SA type specified. Bail(
"write() to PF_KEY socket failed (in doflush)");
Bail(
"read (in doflush)");
* I should _never_ hit the following unless: * 1. There is a kernel bug. * 2. There is another process filling in its pid with mine, and * issuing a different message that would cause a different result. gettext(
"doflush: Return message not of type SADB_FLUSH!"));
Bail(
"doflush: Return message not of type SADB_FLUSH!");
Bail(
"return message (in doflush)");
* save_XXX functions are used when "saving" the SA tables to either a * file or standard output. They use the dump_XXX functions where needed, * but mostly they use the rparseXXX functions. * Because "save" and "dump" both use the SADB_DUMP message, fold both * into the same function. gettext(
"# This key file was generated by the"));
gettext(
" ipseckey(1m) command's 'save' feature.\n\n"));
Bail(
"write to PF_KEY socket failed (in dodump)");
* For DUMP, do only the read as a time critical section. Bail(
"read (in dodump)");
gettext(
"Dump succeeded for SA type %d.\n"),
/* Don't return anything regarding multicast for now... */ /* For now, return global by default. */ * Used by doaddup() and dodelget() to create new SA's based on the * provided source and destination addresses hostent. * sadb_msg_type: expected PF_KEY reply message type * sadb_msg_satype: expected PF_KEY reply satype * srchp: hostent for the source address(es) * dsthp: hostent for the destination address(es) * src: points to the SADB source address extension * dst: points to the SADB destination address extension * unspec_src: indicates an unspecified source address. * buffer: pointer to the SADB buffer to use with PF_KEY * buffer_size: size of buffer * spi: spi for this message (set by caller) * srcport: source port if specified * dstport: destination port if specified * proto: IP protocol number if specified * iproto: Inner (tunnel mode) IP protocol number if specified * NATT note: we are going to assume a semi-sane world where NAT * boxen don't explode to multiple addresses. char **
walker;
/* For the SRC and PROXY walking functions. */ * Okay, now we have "src", "dst", and maybe "proxy" reassigned * to point into the buffer to be written to PF_KEY, we can do * potentially several writes based on destination address. * First, obtain port numbers from passed-in extensions. * The rules for ADD, GET, and UPDATE: (NOTE: This assumes IPsec. * If other consumers of PF_KEY happen, this will have to be * Do a message for every possible DST address. * If a source or proxy address explodes, keep unspecified * (and mention unspecified). * If dsthp is == dummy.he, then go through the loop once. * If any other hp is == dummy.he, then you don't have to apply any * DELETE is different, because you can leave either "src" or "dst" * blank! You need to explode if one of them is full, and not assume * No destination address specified. * With extended diagnostics, we don't have to bail the * non-DELETE cases here. The EINVAL diagnostics will be * enough to inform the user(s) what happened. /* Degenerate case, h_addr_list[0] == NULL. */ Bail(
"Empty source address list");
* Fill in the src sockaddr. /* Save off a copy for later writing... */ Bail(
"write() to PF_KEY socket " * Sends the message to the Solaris Cluster daemon Bail(
"read (in doaddresses)");
"doaddresses: Unexpected returned message " Bail(
"doaddresses: Unexpected returned " "values is incorrect."));
Bail(
"return message (in doaddresses)");
/* ...and then restore the saved buffer. */ * Fill in the dst sockaddr. * Try and assign src, if there's any ambiguity. * IPv4 address. Find an IPv4 address, then * keep looking for a second one. If a second * exists, print a message, and fill in the /* LINTED E_BAD_PTR_CAST_ALIGN */ * No IPv4 hits. Is this a single "No IPv4 source address " "Only single destination " /* Continue, but do I print? */ /* I should never reach here. */ * Early loop exit. It must've been * Issue a null-source warning? "Multiple IPv4 source addresses " "for %s, using unspecified source " * If I reach here w/o hitting the * previous if statements, I have a * single source address for this * IPv6 address. Find an IPv6 address. * Unlike IPv4 addresses, things can get a * little more sticky with scopes, etc. /* LINTED E_BAD_PTR_CAST_ALIGN */ * Take into account scopes, * and other IPv6 thingies. /* LINTED E_BAD_PTR_CAST */ * No IPv6 hits. Is this a single "No IPv6 source address of " "matching scope for name %s.\n"),
/* Continue, but do I print? */ /* I should never reach here. */ * Early loop exit. Issue a "Multiple IPv6 source addresses " "for %s of the same scope, using " "unspecified source instead.\n"),
* If I reach here w/o hitting the * previous if statements, I have a * single source address for this * If there are errors at this point there is no * point sending anything to PF_KEY. /* Save off a copy for later writing... */ Bail(
"write() to PF_KEY socket (in doaddresses)");
/* Blank the key for paranoia's sake. */ Bail(
"read (in doaddresses)");
* I should _never_ hit the following unless: * 1. There is a kernel bug. * 2. Another process is mistakenly using my pid in a PF_KEY "doaddresses: Unexpected returned message " Bail(
"doaddresses: Unexpected returned message");
* Print different error messages depending * on the SADB message type being processed. * messages, we report that the SA does not * exist. If we get a EEXIST error for a "Association (type = %s) " "with spi 0x%x and addr\n"),
"PF_KEY Diagnostic code %u: %s.\n"),
Bail(
"return message (in doaddresses)");
"SA information bigger than %d bytes.\n"),
/* ...and then restore the saved buffer. */ /* Degenerate case, h_addr_list[0] == NULL. */ Bail(
"Empty destination address list");
* free(ebuf) even if there are no errors. * handle_errors() won't return here. * Perform an add or an update. ADD and UPDATE are similar in the extensions /* MLS TODO: Need sensitivity eventually. */ /* Assume last element in argv is set to NULL. */ /* Do nothing, I'm done. */ "Unknown extension field \"%s\" \n"), *(
argv -
1));
* May want to place this chunk of code in a function. * This code checks for duplicate entries on a command /* Allocate the SADB_EXT_SA extension. */ * If some cretin types in "spi 0" then he/she * can type in another SPI. /* Must convert SPI to network order! */ "Invalid SPI value \"0\" .\n"));
"pair-spi can not be used with the " "\"update-pair\" command.\n"));
"single pair SPI value.\n"));
/* Must convert SPI to network order! */ "Invalid SPI value \"0\" .\n"));
* That same cretin can do the same with "single replay window size.\n"));
"WARNING: Replay with manual" " keying considered harmful.\n"));
* 0 is an actual state value, LARVAL. This * means that one can type in the larval state * and then type in another state on the same "single auth algorithm.\n"));
" encryption with SA type ah.\n"));
"single encryption algorithm.\n"));
"Can only specify single" /* set assoc flags later */ "single source port.\n"));
"single destination port.\n"));
"single inner-source port.\n"));
"single inner-destination port.\n"));
"single NAT-T local port.\n"));
"single NAT-T remote port.\n"));
"single inner protocol.\n"));
"single source address.\n"));
"Unknown src address \"%s\"\n"), *
argv);
* Round of the sockaddr length to an 8 byte * boundary to make PF_KEY happy. * Single address with -n flag. "Can only specify single " "destination address.\n"));
"Unknown dst address \"%s\"\n"), *
argv);
* Single address with -n flag. "Can only specify single " /* Parse out the prefix. */ "Invalid prefix %s."),
pstr);
* Assume mapping to AF_INET6, and we're a host. * XXX some miscreants may still make classful * assumptions. If this is a problem, fix it * Single address with -n flag or single name. * normalize prefixlen for IPv4-mapped "Can only specify single " "inner-destination address.\n"));
/* Parse out the prefix. */ "Invalid prefix %s.\n"),
pstr);
* Assume mapping to AF_INET6, and we're a host. * XXX some miscreants may still make classful * assumptions. If this is a problem, fix it "Unknown Inner Src address " * Single address with -n flag or single name. * normalize prefixlen for IPv4-mapped * If the idst address is vague, don't bother. "Inner destination address %s " "single NAT-T local address.\n"));
"Unknown NAT-T local address \"%s\"\n"),
* Round of the sockaddr length to an 8 byte * boundary to make PF_KEY happy. Bail(
"malloc(natt_local)");
* Single address with -n flag or single name. * If the nat-local address is vague, don't "NAT-T local address %s " "is vague, not using.\n"),
"single NAT-T remote address.\n"));
"Unknown NAT-T remote address \"%s\"\n"),
* Round of the sockaddr length to an 8 byte * boundary to make PF_KEY happy. Bail(
"malloc(natt_remote)");
* Single address with -n flag or single name. * If the nat-renote address is vague, don't "NAT-T remote address %s " "is vague, not using.\n"),
"single encryption key.\n"));
"Cannot specify a key with NULL " "encryption algorithm.\n"));
"Invalid encryption key.\n"));
"Can only specify single" " authentication key.\n"));
"Invalid authentication key.\n"));
"Unexpected end of command " "line - Expecting Src Type.\n"));
"Can only specify single" " source certificate identity.\n"));
"Unexpected end of command" " line - expecting dst type.\n"));
"Can only specify single destination " "certificate identity.\n"));
Bail(
"malloc(hard_lifetime)");
"Can only specify single" " hard allocation limit.\n"));
"single hard byte limit.\n"));
"single past-add lifetime.\n"));
"single past-use lifetime.\n"));
Bail(
"malloc(soft_lifetime)");
"Can only specify single" " soft allocation limit.\n"));
"Can only specify single" "Can only specify single" " past-add lifetime.\n"));
"Can only specify single" " past-use lifetime.\n"));
"Can only specify single " Bail(
"malloc(replay value)");
* We currently do not support a 64-bit * replay value. RFC 4301 will require one, * however, and we have a field in place when Bail(
"malloc idle lifetime");
"Reserved bits need to be " "specified before key.\n"));
gettext(
"Malformed security label\n"));
Bail(
"Internal token value error");
gettext(
"Malformed security label\n"));
Bail(
"Internal token value error");
Bail(
"malloc(implicit port)"); \
/* sin/sin6 has equivalent offsets for ports! */ \
* If we specify inner ports or NAT ports w/o addresses, we still need * to allocate. Also, if we have one inner address, we need the * other, even if we don't specify anything. "with any NAT-T port.\n"));
"with any NAT-T address.\n"));
* Okay, so now I have all of the potential extensions! * Allocate a single contiguous buffer. Keep in mind that it'll * be enough because the key itself will be yanked. * Set explicit unspecified source address. Bail(
"malloc(implicit src)");
/* Confusing, but we're copying from DST to SRC. :) */ "The SPI value is missing for " "the association you wish to %s.\n"),
thiscmd);
"Select at least one algorithm " /* Hack to let user specify NULL ESP implicitly. */ /* 0 is an actual value. Print a warning if it was entered. */ "WARNING: Cannot set LARVAL SA state.\n"));
* For now, assume RFC 3884's dream of transport-mode * SAs with inner IP address selectors will not "WARNING: Protocol type %d not " "for use with Tunnel-Mode SA.\n"),
proto);
/* Continue and let PF_KEY scream... */ /* Save the SPI for the case of an error. */ "Need to define SPI for %s.\n"),
thiscmd);
"Need SA parameters for %s.\n"),
thiscmd);
"The SPI value is missing for the " "association you wish to %s.\n"),
thiscmd);
"Must have at least one key for an add.\n"));
"Need destination address for %s.\n"),
thiscmd);
"Must specify NAT-T remote or local address " "for UDP encapsulation.\n"));
* PF_KEY requires a source address extension, even if the source * address itself is unspecified. (See "Set explicit unspecified..." * code fragment above. Destination reality check was above.) * Assume the checked cmd would have worked if it was actually * used. doaddresses() will increment lines_added if it * DELETE and GET are similar, in that they only need the extensions * required to _find_ an SA, and then either delete it or obtain its /* Set the first extension header to right past the base message. */ /* Assume last element in argv is set to NULL. */ /* Do nothing, I'm done. */ "Unknown extension field \"%s\"\n"), *(
argv -
1));
"Can only specify single SPI value.\n"));
"Can only specify single source port.\n"));
"specify single destination port.\n"));
"Can only specify single protocol.\n"));
"Can only specify single source addr.\n"));
"Unknown source address \"%s\"\n"), *
argv);
* Single address with -n flag. /* The rest is pre-bzeroed for us. */ "Can only specify single destination " "Unknown destination address \"%s\"\n"),
* Single address with -n flag. /* The rest is pre-bzeroed for us. */ "Don't use extension %s for '%s' command.\n"),
/* So I have enough of the message to send it down! */ "Need SA parameters for %s.\n"),
thiscmd);
* Assume the checked cmd would have worked if it was actually * used. doaddresses() will increment lines_added if it * "ipseckey monitor" should exit very gracefully if ^C is tapped provided * it is not running in interactive mode. * Loop forever, listening on PF_KEY messages. Bail(
"write (SADB_X_PROMISC)");
* I assume that read() is non-blocking, and will never Bail(
"read (in domonitor)");
* Q: Should I use the same method of printing as GET does? /* restore SIGINT behavior */ * Either mask or unmask all relevant signals. * Assorted functions to print help text. puts_tr(
"update - Update an existing SA");
puts_tr(
"update-pair - Update an existing pair of SA's");
puts_tr(
"add - Add a new security association (SA)");
puts_tr(
"delete-pair - Delete a pair of SA's");
puts_tr(
"esp delete just ESP SAs");
puts_tr(
"<number> delete just SAs with type given by number");
puts_tr(
"esp display just ESP SAs");
puts_tr(
"<number> display just SAs with type " puts_tr(
"monitor - Monitor all PF_KEY reply messages.");
"pmonitor, passive_monitor - Monitor PF_KEY messages that");
" reply to all PF_KEY sockets.");
puts_tr(
"quit, exit - Exit the program");
puts_tr(
"save - Saves all SAs to a file");
puts_tr(
"help - Display list of commands");
puts_tr(
"help <cmd> - Display help for command");
puts_tr(
"help attr - Display possible SA attributes");
puts_tr(
"?, help - Display this list");
puts_tr(
"help <cmd> - Display help for command");
puts_tr(
"help attr - Display possible SA attributes");
puts_tr(
"quit, exit - Exit the program");
puts_tr(
"monitor - Monitor all PF_KEY reply messages.");
puts_tr(
"pmonitor, passive_monitor - Monitor PF_KEY messages that");
puts_tr(
" reply to all PF_KEY sockets.");
puts_tr(
"The following commands are of the form:");
puts_tr(
" <command> {SA type} {attribute value}*");
puts_tr(
"add (interactive only) - Add a new security association (SA)");
puts_tr(
"update (interactive only) - Update an existing SA");
puts_tr(
"update-pair (interactive only) - Update an existing SA pair");
puts_tr(
"delete-pair - Delete an SA pair");
puts_tr(
"save - Saves all SAs to a file");
* "Parse" a command line from argv. * Some commands loop forever and should only be run from the command * line, they should never be run from a command file as this may * Return from the function in interactive mode to * avoid error message in the next switch statement. * Also print newline to prevent prompt clobbering. * The same is done for CMD_PMONITOR. * You must specify either "all" or a specific SA type * for the "save" command. "Must specify a specific " * If this was called because of an entry in a cmd file * then this action needs to be counted to prevent * do_interactive() treating this as an error. * NOTE: Shouldn't allow ADDs or UPDATEs with keying material "can't do ADD or UPDATE from the command line.\n"));
"Must specify a specific SA type."));
/* Parse for extensions, including keying material. */ "Must specify a single SA type."));
/* Parse for bare minimum to locate an SA. */ * Check to see if the command is being run from smf(5). errx(
1,
"Insufficient privileges to run ipseckey.");
/* umask me to paranoid, I only want to create files read-only */ * Use stat() to check and see if the user inadvertently * passed in a bad pathname, or the name of a directory. * We should also check to see if the filename is a * pipe. We use stat() here because fopen() will block * unless the other end of the pipe is open. This would * be undesirable, especially if this is called at boot * time. If we ever need to support reading from a pipe * or special file, this should be revisited. * The input file contains keying information, because * this is sensative, we should only accept data from * this file if the file is root owned and only readable * by privileged users. If the command is being run by * the administrator, issue a warning, if this is run by * smf(5) (IE: boot time) and the permissions are too * open, we will fail, the SMF service will end up in * maintenace mode. The check is made with fstat() to * eliminate any possible TOT to TOU window. "%s has insecure permissions.",
"Config file %s has insecure " "permissions, will be rejected in " "permanent config.\n"),
optarg);
* When run from smf(5) flush any existing SA's first * otherwise you will end up in maintenance mode. "Flushing existing SA's before adding new SA's\n"));
/* Go into interactive mode here. */