From 7a295e2c5db878dbd076c2f582cbae8aa738c3b7 Mon Sep 17 00:00:00 2001 From: Xan Lopez Date: Tue, 15 Dec 2009 10:31:47 +0100 Subject: Move profile migration tools from src/ to lib/ We'll use them from embed/ for form password saving, and embed/ can't use code from src/ --- lib/ephy-nss-glue.c | 309 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 309 insertions(+) create mode 100644 lib/ephy-nss-glue.c (limited to 'lib/ephy-nss-glue.c') diff --git a/lib/ephy-nss-glue.c b/lib/ephy-nss-glue.c new file mode 100644 index 000000000..e1c65b6c0 --- /dev/null +++ b/lib/ephy-nss-glue.c @@ -0,0 +1,309 @@ +/* + * Copyright © 2009 Igalia S.L. + * + * 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, 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. + * + */ + +/* Portions of this file based on Chromium code. + * License block as follows: + * + * Copyright (c) 2009 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * The LICENSE file from Chromium can be found in the LICENSE.chromium + * file. + */ + +#include "config.h" + +#include "ephy-nss-glue.h" + +#include "ephy-file-helpers.h" + +#include +#include +#include +#include + +static gboolean nss_initialized = FALSE; +static PK11SlotInfo *db_slot = NULL; + +static char* +ask_for_nss_password (PK11SlotInfo *slot, + PRBool retry, + void *arg) +{ + GtkWidget *dialog; + GtkWidget *entry; + gint result; + char *password = NULL; + + if (retry) + return NULL; + + dialog = gtk_message_dialog_new (NULL, + 0, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_OK_CANCEL, + _("Master password needed")); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + _("The passwords from the previous version (Gecko) are locked with a master password. If you want Epiphany to import them, please enter your master password below.")); + entry = gtk_entry_new (); + gtk_entry_set_visibility (GTK_ENTRY (entry), FALSE); + gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), + entry); + gtk_widget_show (entry); + + result = gtk_dialog_run (GTK_DIALOG (dialog)); + + switch (result) { + case GTK_RESPONSE_OK: + password = PL_strdup (gtk_entry_get_text (GTK_ENTRY (entry))); + break; + default: + break; + } + + gtk_widget_destroy (dialog); + return password; +} + +gboolean ephy_nss_glue_init () +{ + char *config_dir, *modspec; + SECStatus rv; + + config_dir = g_build_filename (ephy_dot_dir (), + "mozilla", "epiphany", + NULL); + rv = NSS_Init (config_dir); + + if (rv < 0) { + g_free (config_dir); + return FALSE; + } + + modspec = g_strdup_printf ("configDir='%s' tokenDescription='Firefox NSS database' " + "flags=readOnly", config_dir); + db_slot = SECMOD_OpenUserDB (modspec); + g_free (config_dir); + g_free (modspec); + + if (!db_slot) + return FALSE; + + /* It's possibly to set a master password for NSS through the + certificate manager extension, so we must support that too */ + PK11_SetPasswordFunc (ask_for_nss_password); + + nss_initialized = TRUE; + + return TRUE; +} + +void ephy_nss_glue_close () +{ + if (db_slot) { + PK11_FreeSlot (db_slot); + db_slot = NULL; + } + + PK11_SetPasswordFunc (NULL); + + NSS_Shutdown (); + + nss_initialized = FALSE; +} + +typedef struct SDRResult +{ + SECItem keyid; + SECAlgorithmID alg; + SECItem data; +} SDRResult; + +static SEC_ASN1Template g_template[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof (SDRResult) }, + { SEC_ASN1_OCTET_STRING, offsetof(SDRResult, keyid) }, + { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(SDRResult, alg), + SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }, + { SEC_ASN1_OCTET_STRING, offsetof(SDRResult, data) }, + { 0 } +}; + +static SECStatus +unpadBlock(SECItem *data, int blockSize, SECItem *result) +{ + SECStatus rv = SECSuccess; + int padLength; + int i; + + result->data = 0; + result->len = 0; + + /* Remove the padding from the end if the input data */ + if (data->len == 0 || data->len % blockSize != 0) { rv = SECFailure; goto loser; } + + padLength = data->data[data->len-1]; + if (padLength > blockSize) { rv = SECFailure; goto loser; } + + /* verify padding */ + for (i=data->len - padLength; (uint32)i < data->len; i++) { + if (data->data[i] != padLength) { + rv = SECFailure; + goto loser; + } + } + + result->len = data->len - padLength; + result->data = (unsigned char *)PORT_Alloc(result->len); + if (!result->data) { rv = SECFailure; goto loser; } + + PORT_Memcpy(result->data, data->data, result->len); + + if (padLength < 2) { + /* Chromium returns an error here, but it seems to be harmless and + if we continue we'll be able to import the password + correctly */ + /* return SECWouldBlock; */ + } + +loser: + return rv; +} + +static SECStatus +pk11Decrypt (PK11SlotInfo *slot, PLArenaPool *arena, + CK_MECHANISM_TYPE type, PK11SymKey *key, + SECItem *params, SECItem *in, SECItem *result) +{ + PK11Context *ctx = 0; + SECItem paddedResult; + SECStatus rv; + + paddedResult.len = 0; + paddedResult.data = 0; + + ctx = PK11_CreateContextBySymKey (type, CKA_DECRYPT, key, params); + if (!ctx) { + rv = SECFailure; + goto loser; + } + + paddedResult.len = in->len; + paddedResult.data = (unsigned char*)PORT_ArenaAlloc (arena, paddedResult.len); + + rv = PK11_CipherOp (ctx, paddedResult.data, + (int*)&paddedResult.len, paddedResult.len, + in->data, in->len); + if (rv != SECSuccess) + goto loser; + + PK11_Finalize(ctx); + + /* Remove the padding */ + rv = unpadBlock (&paddedResult, PK11_GetBlockSize(type, 0), result); + if (rv) + goto loser; + +loser: + if (ctx) + PK11_DestroyContext (ctx, PR_TRUE); + + return rv; +} + +static SECStatus +PK11SDR_DecryptWithSlot (PK11SlotInfo *slot, SECItem *data, SECItem *result, void *cx) +{ + SECStatus rv = SECSuccess; + PK11SymKey *key = NULL; + CK_MECHANISM_TYPE type; + SDRResult sdrResult; + SECItem *params = NULL; + SECItem possibleResult = { (SECItemType)0, NULL, 0 }; + PLArenaPool *arena = NULL; + + arena = PORT_NewArena (SEC_ASN1_DEFAULT_ARENA_SIZE); + if (!arena) { + rv = SECFailure; + goto loser; + } + + /* Decode the incoming data */ + memset (&sdrResult, 0, sizeof sdrResult); + rv = SEC_QuickDERDecodeItem (arena, &sdrResult, g_template, data); + if (rv != SECSuccess) + goto loser; /* Invalid format */ + + /* Get the parameter values from the data */ + params = PK11_ParamFromAlgid (&sdrResult.alg); + if (!params) { + rv = SECFailure; + goto loser; + } + + /* Use triple-DES (Should look up the algorithm) */ + type = CKM_DES3_CBC; + key = PK11_FindFixedKey (slot, type, &sdrResult.keyid, cx); + if (!key) { + rv = SECFailure; + } else { + rv = pk11Decrypt (slot, arena, type, key, params, + &sdrResult.data, result); + } + + loser: + if (arena) + PORT_FreeArena (arena, PR_TRUE); + + if (key) + PK11_FreeSymKey(key); + + if (params) + SECITEM_ZfreeItem(params, PR_TRUE); + + if (possibleResult.data) + SECITEM_ZfreeItem(&possibleResult, PR_FALSE); + + return rv; +} + +char * ephy_nss_glue_decrypt (const unsigned char *data, gsize length) +{ + char *plain = NULL; + SECItem request, reply; + SECStatus result; + + result = PK11_Authenticate (db_slot, PR_TRUE, NULL); + if (result != SECSuccess) + return NULL; + + request.data = (unsigned char*)data; + request.len = length; + reply.data = NULL; + reply.len = 0; + + result = PK11SDR_DecryptWithSlot (db_slot, &request, &reply, NULL); + if (result == SECSuccess) + plain = g_strndup ((const char*)reply.data, reply.len); + + SECITEM_FreeItem (&reply, PR_FALSE); + + return plain; +} + -- cgit