251 lines
6.5 KiB
C
251 lines
6.5 KiB
C
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
|
|
/*
|
|
* Dan Williams <dcbw@redhat.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* (C) Copyright 2010 Red Hat, Inc.
|
|
*/
|
|
|
|
#include "nm-default.h"
|
|
|
|
#include "utils.h"
|
|
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <arpa/inet.h>
|
|
|
|
#include "nm-utils/nm-shared-utils.h"
|
|
|
|
gboolean
|
|
is_pkcs12 (const char *filepath)
|
|
{
|
|
NMSetting8021xCKFormat ck_format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
|
|
NMSetting8021x *s_8021x;
|
|
|
|
if (!filepath || !strlen (filepath))
|
|
return FALSE;
|
|
|
|
if (!g_file_test (filepath, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))
|
|
return FALSE;
|
|
|
|
s_8021x = (NMSetting8021x *) nm_setting_802_1x_new ();
|
|
g_return_val_if_fail (s_8021x != NULL, FALSE);
|
|
|
|
nm_setting_802_1x_set_private_key (s_8021x,
|
|
filepath,
|
|
NULL,
|
|
NM_SETTING_802_1X_CK_SCHEME_PATH,
|
|
&ck_format,
|
|
NULL);
|
|
g_object_unref (s_8021x);
|
|
|
|
return (ck_format == NM_SETTING_802_1X_CK_FORMAT_PKCS12);
|
|
}
|
|
|
|
#define PROC_TYPE_TAG "Proc-Type: 4,ENCRYPTED"
|
|
#define PKCS8_TAG "-----BEGIN ENCRYPTED PRIVATE KEY-----"
|
|
|
|
/** Checks if a file appears to be an encrypted private key.
|
|
* @param filename the path to the file
|
|
* @return returns true if the key is encrypted, false otherwise
|
|
*/
|
|
gboolean
|
|
is_encrypted (const char *filename)
|
|
{
|
|
GIOChannel *pem_chan;
|
|
char *str = NULL;
|
|
gboolean encrypted = FALSE;
|
|
|
|
if (!filename || !strlen (filename))
|
|
return FALSE;
|
|
|
|
if (is_pkcs12 (filename))
|
|
return TRUE;
|
|
|
|
pem_chan = g_io_channel_new_file (filename, "r", NULL);
|
|
if (!pem_chan)
|
|
return FALSE;
|
|
|
|
while (g_io_channel_read_line (pem_chan, &str, NULL, NULL, NULL) != G_IO_STATUS_EOF) {
|
|
if (str) {
|
|
if (g_str_has_prefix (str, PROC_TYPE_TAG) || g_str_has_prefix (str, PKCS8_TAG)) {
|
|
encrypted = TRUE;
|
|
break;
|
|
}
|
|
g_free (str);
|
|
}
|
|
}
|
|
|
|
g_io_channel_shutdown (pem_chan, FALSE, NULL);
|
|
g_io_channel_unref (pem_chan);
|
|
return encrypted;
|
|
}
|
|
|
|
static gboolean
|
|
_is_inet6_addr (const char *str, gboolean with_square_brackets)
|
|
{
|
|
struct in6_addr a;
|
|
gsize l;
|
|
|
|
if ( with_square_brackets
|
|
&& str[0] == '[') {
|
|
l = strlen (str);
|
|
if (str[l - 1] == ']') {
|
|
gs_free char *s = g_strndup (&str[1], l - 2);
|
|
|
|
return inet_pton (AF_INET6, s, &a) == 1;
|
|
}
|
|
}
|
|
return inet_pton (AF_INET6, str, &a) == 1;
|
|
}
|
|
|
|
/**
|
|
* nmovpn_remote_parse:
|
|
* @str: the input string to be split. It is modified inplace.
|
|
* @out_buf: an allocated string, to which the other arguments
|
|
* point to. Must be freeded by caller.
|
|
* @out_host: pointer to the host out argument.
|
|
* @out_port: pointer to the port out argument.
|
|
* @out_proto: pointer to the proto out argument.
|
|
* @error:
|
|
*
|
|
* Splits @str in three parts host, port and proto.
|
|
*
|
|
* Returns: -1 on success or index in @str of first invalid character.
|
|
* Note that the error index can be at strlen(str), if some data is missing.
|
|
**/
|
|
gssize
|
|
nmovpn_remote_parse (const char *str,
|
|
char **out_buf,
|
|
const char **out_host,
|
|
const char **out_port,
|
|
const char **out_proto,
|
|
GError **error)
|
|
{
|
|
gs_free char *str_copy = NULL;
|
|
char *t;
|
|
char *host = NULL;
|
|
char *port = NULL;
|
|
char *proto = NULL;
|
|
gssize idx_fail;
|
|
|
|
g_return_val_if_fail (str, 0);
|
|
if (!out_buf) {
|
|
/* one can omit @out_buf only if also no other out-arguments
|
|
* are requested. */
|
|
if (out_host || out_port || out_proto)
|
|
g_return_val_if_reached (0);
|
|
}
|
|
g_return_val_if_fail (!error || !*error, 0);
|
|
|
|
t = strchr (str, ' ');
|
|
if (!t)
|
|
t = strchr (str, ',');
|
|
if (t) {
|
|
g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
|
|
_("invalid delimiter character '%c'"), t[0]);
|
|
idx_fail = t - str;
|
|
goto out_fail;
|
|
}
|
|
|
|
if (!g_utf8_validate (str, -1, (const char **) &t)) {
|
|
g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
|
|
_("invalid non-utf-8 character"));
|
|
idx_fail = t - str;
|
|
goto out_fail;
|
|
}
|
|
|
|
str_copy = g_strdup (str);
|
|
|
|
/* we already checked that there is no space above.
|
|
* Strip tabs nonetheless. */
|
|
host = nm_str_skip_leading_spaces (str_copy);
|
|
g_strchomp (host);
|
|
|
|
t = strrchr (host, ':');
|
|
if ( t
|
|
&& !_is_inet6_addr (host, TRUE)) {
|
|
t[0] = '\0';
|
|
port = &t[1];
|
|
t = strrchr (host, ':');
|
|
if ( t
|
|
&& !_is_inet6_addr (host, TRUE)) {
|
|
t[0] = '\0';
|
|
proto = port;
|
|
port = &t[1];
|
|
}
|
|
}
|
|
|
|
if (!host[0]) {
|
|
g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
|
|
_("empty host"));
|
|
idx_fail = host - str;
|
|
goto out_fail;
|
|
}
|
|
if (port) {
|
|
if (!port[0]) {
|
|
/* allow empty port like "host::udp". */
|
|
port = NULL;
|
|
} else if (_nm_utils_ascii_str_to_int64 (port, 10, 1, 0xFFFF, 0) == 0) {
|
|
g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
|
|
_("invalid port"));
|
|
idx_fail = port - str;
|
|
goto out_fail;
|
|
}
|
|
}
|
|
if (proto) {
|
|
if (!proto[0]) {
|
|
/* allow empty proto, so that host can contain ':'. */
|
|
proto = NULL;
|
|
} else if (!NM_IN_STRSET (proto, NMOVPN_PROTCOL_TYPES)) {
|
|
g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
|
|
_("invalid protocol"));
|
|
idx_fail = proto - str;
|
|
goto out_fail;
|
|
}
|
|
}
|
|
|
|
if (out_buf) {
|
|
*out_buf = g_steal_pointer (&str_copy);
|
|
if ( host[0] == '['
|
|
&& _is_inet6_addr (host, TRUE)
|
|
&& !_is_inet6_addr (host, FALSE)) {
|
|
gsize l;
|
|
|
|
host++;
|
|
l = strlen (host);
|
|
nm_assert (l > 0 && host[l - 1] == ']');
|
|
host[l - 1] = '\0';
|
|
nm_assert (_is_inet6_addr (host, FALSE));
|
|
}
|
|
NM_SET_OUT (out_host, host);
|
|
NM_SET_OUT (out_port, port);
|
|
NM_SET_OUT (out_proto, proto);
|
|
}
|
|
return -1;
|
|
|
|
out_fail:
|
|
if (out_buf) {
|
|
*out_buf = NULL;
|
|
NM_SET_OUT (out_host, NULL);
|
|
NM_SET_OUT (out_port, NULL);
|
|
NM_SET_OUT (out_proto, NULL);
|
|
}
|
|
return idx_fail;
|
|
}
|
|
|
|
/*****************************************************************************/
|