opensc/src/tools/pkcs11-register.c

352 lines
9.5 KiB
C

/*
* Copyright (C) 2019 Frank Morgner <frankmorgner@gmail.com>
*
* This file is part of OpenSC.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "fread_to_eof.h"
#include "pkcs11-register-cmdline.h"
#include <limits.h>
#include <string.h>
#include <stdlib.h>
#ifdef _WIN32
#include <windows.h>
const char path_sep = '\\';
#else
const char path_sep = '/';
#endif
const char *default_pkcs11_provider = DEFAULT_PKCS11_PROVIDER;
const char *default_onepin_pkcs11_provider = DEFAULT_ONEPIN_PKCS11_PROVIDER;
int
get_profiles_ini(const char *home, const char *basedir, char **profiles_ini)
{
size_t profiles_ini_len = 0;
char profiles_ini_path[PATH_MAX];
if (home && basedir
&& 0 <= snprintf(profiles_ini_path, sizeof profiles_ini_path,
"%s%c%s%c%s", home, path_sep, basedir, path_sep, "profiles.ini")
&& fread_to_eof(profiles_ini_path,
(unsigned char **) profiles_ini, &profiles_ini_len)) {
char *p = realloc(*profiles_ini, profiles_ini_len+1);
if (p) {
p[profiles_ini_len] = '\0';
*profiles_ini = p;
return 1;
}
}
return 0;
}
const char *
get_next_profile_path(const char **profiles_ini, const char *home, const char *basedir)
{
static char profile_path[PATH_MAX];
if (!home || !profiles_ini)
return NULL;
while (*profiles_ini) {
const char *this_profile = strstr(*profiles_ini, "[");
if (!this_profile) {
return NULL;
}
const char *next_profile = strstr(this_profile + 1, "[");
const char *is_relative = strstr(this_profile, "IsRelative=1");
const char *path = strstr(this_profile, "Path=");
/* advance profile_ini for the next iteration */
if (next_profile) {
*profiles_ini = next_profile;
if (next_profile < path) {
/* path belongs to the next profile */
path = NULL;
}
if (next_profile < is_relative) {
/* IsRelative belongs to the next profile */
is_relative = NULL;
}
} else {
*profiles_ini = NULL;
}
if (!path)
continue;
/* build the path to the profile */
char *p = profile_path;
size_t p_len = sizeof profile_path;
if (is_relative) {
size_t l = strlen(home) + sizeof path_sep + strlen(basedir) + sizeof path_sep;
if (0 > snprintf(p, p_len, "%s%c%s%c", home, path_sep, basedir, path_sep))
continue;
p_len -= l;
p += l;
}
/* adjust format to respect the maximum length of profile_path */
char format[32];
if (0 > snprintf(format, sizeof(format), "Path=%%%ds", (int)(p_len-1))
|| 1 != sscanf(path, format, p))
continue;
return profile_path;
}
return NULL;
}
void
add_module_pkcs11_txt(const char *profile_dir,
const char *module_path, const char *module_name, const char *exclude_module_path)
{
char pkcs11_txt_path[PATH_MAX];
char *pkcs11_txt = NULL;
size_t pkcs11_txt_len = 0;
unsigned char *txt = NULL;
if (!profile_dir
|| snprintf(pkcs11_txt_path, sizeof pkcs11_txt_path,
"%s%c%s", profile_dir, path_sep, "pkcs11.txt") < 0
|| !fread_to_eof(pkcs11_txt_path, &txt, &pkcs11_txt_len)) {
goto err;
}
pkcs11_txt = (char *)txt;
char *p = realloc(pkcs11_txt, pkcs11_txt_len+1);
if (!p)
goto err;
p[pkcs11_txt_len] = '\0';
pkcs11_txt = p;
if (!strstr(pkcs11_txt, module_path)
&& (!exclude_module_path || !strstr(pkcs11_txt, exclude_module_path))) {
/* module is not yet present */
FILE *f = fopen(pkcs11_txt_path, "a");
if (f) {
if (fprintf(f,
"library=%s\n"
"name=%s\n"
"\n", module_path, module_name) >= 0) {
printf("Added %s to %s\n", module_name, pkcs11_txt_path);
}
fclose(f);
}
}
err:
free(pkcs11_txt);
}
struct location {
const char *var;
const char *dir;
};
void
add_module_mozilla(const struct location *locations, size_t locations_len,
const char *module_path, const char *module_name, const char *exclude_module_path)
{
size_t i;
for (i = 0; i < locations_len; i++) {
char *profiles_ini = NULL;
const char *home = getenv(locations[i].var);
if (!home)
continue;
if (get_profiles_ini(home, locations[i].dir, &profiles_ini)) {
const char *p = profiles_ini;
while (1) {
const char *profile_path = get_next_profile_path(&p, home, locations[i].dir);
if (!profile_path)
break;
add_module_pkcs11_txt(profile_path, module_path, module_name, exclude_module_path);
}
}
free(profiles_ini);
}
}
#include "pkcs11/pkcs11.h"
#include "common/libpkcs11.h"
const char *
get_module_name(const char *module_path)
{
const char *name = NULL;
CK_FUNCTION_LIST_PTR p11 = NULL;
void *module = C_LoadModule(module_path, &p11);
if (module) {
CK_INFO info;
if (CKR_OK == p11->C_Initialize(NULL)
&& CKR_OK == p11->C_GetInfo(&info)) {
static char module_name[32+sizeof " (255.255)"];
int libraryDescription_len = 32;
while (libraryDescription_len > 0
&& info.libraryDescription[libraryDescription_len-1] == ' ')
libraryDescription_len--;
snprintf(module_name, sizeof module_name,
"%.*s (%d.%d)",
libraryDescription_len, info.libraryDescription,
info.libraryVersion.major, info.libraryVersion.minor);
name = module_name;
}
p11->C_Finalize(NULL);
C_UnloadModule(module);
}
return name;
}
void
add_module_firefox(const char *module_path, const char *module_name, const char *exclude_module_path)
{
struct location locations[] = {
#if defined(__APPLE__)
{"HOME", "Library/Application Support/Firefox"},
{"HOME", "Library/Mozilla/Firefox"},
#elif defined(_WIN32)
{"APPDATA", "Mozilla\\Firefox"},
#else
{"HOME", ".mozilla/firefox"},
#endif
};
if (0 == strcmp(module_path, default_pkcs11_provider)) {
module_path = default_onepin_pkcs11_provider;
exclude_module_path = default_pkcs11_provider;
}
add_module_mozilla(locations, sizeof locations/sizeof *locations,
module_path, module_name, exclude_module_path);
}
void
add_module_thunderbird(const char *module_path, const char *module_name, const char *exclude_module_path)
{
struct location locations[] = {
#if defined(__APPLE__)
{"HOME", "Library/Application Support/Thunderbird"},
{"HOME", "Library/Mozilla/Thunderbird"},
#elif defined(_WIN32)
{"APPDATA", "Mozilla\\Thunderbird"},
#else
{"HOME", ".thunderbird"},
{"HOME", ".mozilla-thunderbird"},
#endif
};
add_module_mozilla(locations, sizeof locations/sizeof *locations,
module_path, module_name, exclude_module_path);
}
void
add_module_seamonkey(const char *module_path, const char *module_name, const char *exclude_module_path)
{
struct location locations[] = {
#if defined(__APPLE__)
{"HOME", "Library/Application Support/SeaMonkey"},
{"HOME", "Library/Mozilla/SeaMonkey"},
#elif defined(_WIN32)
{"APPDATA", "Mozilla\\SeaMonkey"},
#else
{"HOME", ".mozilla/seamonkey"},
#endif
};
add_module_mozilla(locations, sizeof locations/sizeof *locations,
module_path, module_name, exclude_module_path);
}
void
add_module_chrome(const char *module_path, const char *module_name, const char *exclude_module_path)
{
#if defined(__APPLE__) || defined(_WIN32)
/* OS specific framework will be used by Chrome instead of PKCS#11 */
#else
char profile_path[PATH_MAX];
const char *home = getenv("HOME");
if (0 == strcmp(module_path, default_pkcs11_provider)) {
module_path = default_onepin_pkcs11_provider;
exclude_module_path = default_pkcs11_provider;
}
if (home && 0 <= snprintf(profile_path, sizeof profile_path,
"%s%c%s", home, path_sep, ".pki/nssdb")) {
add_module_pkcs11_txt(profile_path, module_path, module_name, exclude_module_path);
}
#endif
}
#define expand(path, expanded, len) \
len = ExpandEnvironmentStringsA(path, \
expanded, sizeof expanded); \
if (0 < len && len < sizeof expanded) \
path = expanded;
int
main(int argc, char **argv)
{
struct gengetopt_args_info cmdline;
const char *exclude_module_path = NULL;
if (cmdline_parser(argc, argv, &cmdline) != 0)
return 1;
const char *module_path = cmdline.module_arg;
if (!cmdline.module_given) {
module_path = default_pkcs11_provider;
exclude_module_path = default_onepin_pkcs11_provider;
}
#ifdef _WIN32
DWORD expanded_len;
char module_path_expanded[PATH_MAX], default_expanded[PATH_MAX], onepin_expanded[PATH_MAX];
expand(module_path, module_path_expanded, expanded_len);
expand(default_pkcs11_provider, default_expanded, expanded_len);
expand(default_onepin_pkcs11_provider, onepin_expanded, expanded_len);
#endif
const char *module_name = get_module_name(module_path);
if (!module_name) {
fprintf(stderr, "Could not load initialize %s\n", module_path);
cmdline_parser_free (&cmdline);
return 1;
}
if (!cmdline.skip_chrome_flag)
add_module_chrome(module_path, module_name, exclude_module_path);
if (!cmdline.skip_firefox_flag)
add_module_firefox(module_path, module_name, exclude_module_path);
if (!cmdline.skip_thunderbird_flag)
add_module_thunderbird(module_path, module_name, exclude_module_path);
if (!cmdline.skip_seamonkey_flag)
add_module_seamonkey(module_path, module_name, exclude_module_path);
cmdline_parser_free (&cmdline);
return 0;
}