/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public License * Version 1.0 (the "NPL"); you may not use this file except in * compliance with the NPL. You may obtain a copy of the NPL at * http://www.mozilla.org/NPL/ * * Software distributed under the NPL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL * for the specific language governing rights and limitations under the * NPL. * * The Initial Developer of this code under the NPL is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ /* backend glue code to do password caching * Accepts password queries and does lookups in a secure database * * Written by Lou Montulli Jan 98 */ #include "xp.h" #include "prlog.h" #include "pwcacapi.h" #ifdef XP_MAC #include #endif DB *pw_database=NULL; XP_List *pc_interpret_funcs=NULL; struct _PCNameValuePair { char *name; char *value; }; struct _PCNameValueArray { int32 cur_ptr; int32 size; int32 first_empty; PCNameValuePair *pairs; }; typedef struct { char *module; PCDataInterpretFunc *func; } PCInterpretModuleAssoc; PUBLIC void PC_Shutdown() { if(pw_database) { (*pw_database->close)(pw_database); pw_database = NULL; } } PRIVATE int pc_open_database(void) { char* filename = "passcac.db"; static Bool have_tried_open=FALSE; if(!pw_database) { HASHINFO hash_info = { 4 * 1024, 0, 0, #ifdef WIN16 60 * 1024, #else 96 * 1024, #endif 0, 0}; /* @@@ add .db to the end of the files */ pw_database = dbopen(filename, O_RDWR | O_CREAT, 0600, DB_HASH, &hash_info); if(!have_tried_open && !pw_database) { XP_StatStruct stat_entry; have_tried_open = TRUE; /* only do this once */ PR_LogPrint("Could not open cache database -- errno: %d",errno); /* if the file is zero length remove it */ if(XP_Stat("", &stat_entry, xpCacheFAT) != -1) { if(stat_entry.st_size <= 0) { XP_FileRemove("", xpCacheFAT); } } /* try it again */ if (filename) { pw_database = dbopen(filename, O_RDWR | O_CREAT, 0600, DB_HASH, 0); } else pw_database = NULL; } } /* return non-zero if the pw_database pointer is * non-zero */ return((int) pw_database); } PRIVATE char * pc_gen_key(char *module, char *module_key) { char *combo=NULL; StrAllocCopy(combo, module); StrAllocCat(combo, "\t"); StrAllocCat(combo, module_key); return combo; } PRIVATE void pc_separate_key(char *key, char **module, char **module_key) { char *tab; *module = NULL; *module_key = NULL; if(!key) return; tab = XP_STRCHR(key, '\t'); if(!tab) return; *tab = '\0'; *module = XP_STRDUP(key); *module_key = XP_STRDUP(tab+1); *tab = '\t'; } PRIVATE PCInterpretModuleAssoc * pc_find_interpret_func(char *module) { XP_List *list_ptr = pc_interpret_funcs; PCInterpretModuleAssoc *assoc; if(!module) return NULL; while((assoc = (PCInterpretModuleAssoc *) XP_ListNextObject(list_ptr)) != NULL) { if(!XP_STRCMP(module, assoc->module)) return assoc; } return NULL; } /* returns 0 on success -1 on error */ PUBLIC int PC_RegisterDataInterpretFunc(char *module, PCDataInterpretFunc *func) { PCInterpretModuleAssoc *assoc; if(!pc_interpret_funcs) { pc_interpret_funcs = XP_ListNew(); if(!pc_interpret_funcs) return -1; } if((assoc = pc_find_interpret_func(module)) != NULL) { assoc->func = func; return 0; } assoc = XP_NEW(PCInterpretModuleAssoc); if(!assoc) return -1; assoc->module = XP_STRDUP(module); assoc->func = func; if(!assoc->module) { XP_FREE(assoc); return -1; } XP_ListAddObject(pc_interpret_funcs, assoc); return 0; } PRIVATE void pc_lookup_module_info(char *key, char *data, int32 data_size, char *type_buffer, int type_buffer_size, char *url_buffer, int url_buffer_size, char *username_buffer, int username_buffer_size, char *password_buffer, int password_buffer_size) { char *module, *module_key; PCInterpretModuleAssoc *assoc; *type_buffer = '\0'; *url_buffer = '\0'; *username_buffer = '\0'; *password_buffer = '\0'; pc_separate_key(key, &module, &module_key); if(!module || !module_key) { XP_FREEIF(module); XP_FREEIF(module_key); return; } /* lookup an explain function from the modules list and use it to interpret the data */ /* @@@@ */ if(0 == (assoc = pc_find_interpret_func(module))) { /* cant find one */ return; } (*assoc->func)(module, module_key, data, data_size, type_buffer, type_buffer_size, url_buffer, url_buffer_size, username_buffer, username_buffer_size, password_buffer, password_buffer_size); } /*, returns status */ PUBLIC int PC_DisplayPasswordCacheAsHTML(URL_Struct *URL_s, FO_Present_Types format_out, MWContext *context) { DBT key, data; int status = 1; NET_StreamClass *stream; char tmp_buffer[512]; char type_buffer[256]; char url_buffer[512]; char username_buffer[256]; char password_buffer[256]; format_out = CLEAR_CACHE_BIT(format_out); StrAllocCopy(URL_s->content_type, TEXT_HTML); stream = NET_StreamBuilder(format_out, URL_s, context); if(!stream) { return MK_UNABLE_TO_CONVERT; } /* define a macro to push a string up the stream * and handle errors */ #define PUT_PART(part) \ status = (*stream->put_block)(stream, \ part ? part : "Unknown", \ part ? XP_STRLEN(part) : 7); \ if(status < 0) \ goto END; if(!pc_open_database()) { XP_STRCPY(tmp_buffer, "The password database is currently unopenable"); PUT_PART(tmp_buffer); goto END; } if(0 != (*pw_database->seq)(pw_database, &key, &data, R_FIRST)) { XP_STRCPY(tmp_buffer, "The password database is currently empty"); PUT_PART(tmp_buffer); goto END; } do { pc_lookup_module_info(key.data, data.data, data.size, type_buffer, sizeof(type_buffer), url_buffer, sizeof(url_buffer), username_buffer, sizeof(username_buffer), password_buffer, sizeof(password_buffer)); PUT_PART("Protocol: "); PUT_PART(type_buffer); PUT_PART("
\nURL: "); PUT_PART(url_buffer); PUT_PART("
\nUsername: "); PUT_PART(username_buffer); PUT_PART("
\nPassword: "); PUT_PART(password_buffer); PUT_PART("\n
\n"); } while (0 == (*pw_database->seq)(pw_database, &key, &data, R_NEXT)); END: if(status < 0) (*stream->abort)(stream, status); else (*stream->complete)(stream); return status; } PUBLIC int PC_PromptUsernameAndPassword(MWContext *context, char *prompt, char **username, char **password, XP_Bool *remember, XP_Bool is_secure) { *remember = TRUE; *remember = FALSE; return FE_PromptUsernameAndPassword(context, prompt, username, password); } PUBLIC char * PC_PromptPassword(MWContext *context, char *prompt, XP_Bool *remember, XP_Bool is_secure) { *remember = TRUE; *remember = FALSE; return FE_PromptPassword(context, prompt); } PUBLIC char * PC_Prompt(MWContext *context, char *prompt, char *deflt, XP_Bool *remember, XP_Bool is_secure) { *remember = TRUE; *remember = FALSE; return FE_Prompt(context, prompt, deflt); } PUBLIC void PC_FreeNameValueArray(PCNameValueArray *array) { int index; if(array) { if(array->pairs) { for(index=0; index < array->first_empty; index++) { XP_FREEIF(array->pairs[index].name); XP_FREEIF(array->pairs[index].value); } XP_FREE(array->pairs); } XP_FREE(array); } } #define MIN_ARRAY_SIZE 4 #define GROW_ARRAY_BY 4 PRIVATE PCNameValueArray * pc_new_namevaluearray(int init_size) { PCNameValueArray *array = XP_NEW_ZAP(PCNameValueArray); if(!array) return NULL; array->pairs = (PCNameValuePair*)XP_CALLOC(init_size, sizeof(PCNameValuePair)); array->size = init_size; array->first_empty = 0; array->cur_ptr = 0; if(!array->pairs) { PC_FreeNameValueArray(array); return NULL; } return array; } PUBLIC PCNameValueArray * PC_NewNameValueArray() { return pc_new_namevaluearray(MIN_ARRAY_SIZE); } PUBLIC uint32 PC_ArraySize(PCNameValueArray *array) { return(array->first_empty); } /* returns value for a given name */ PUBLIC char * PC_FindInNameValueArray(PCNameValueArray *array, char *name) { int i; for(i=0; ifirst_empty; i++) { if(!XP_STRCMP(array->pairs[i].name, name)) return XP_STRDUP(array->pairs[i].value); } return NULL; } PUBLIC int PC_DeleteNameFromNameValueArray(PCNameValueArray *array, char *name) { int i; if(!array) return -1; for(i=0; ifirst_empty; i++) { if(!XP_STRCMP(array->pairs[i].name, name)) { /* found it */ /* delete it */ XP_FREE(array->pairs[i].name); XP_FREEIF(array->pairs[i].value); /* move everything */ array->first_empty--; if(array->first_empty > i+1) XP_MEMCPY(&array->pairs[i], &array->pairs[i+1], (array->first_empty - (i+1)) * sizeof(PCNameValuePair)); return 0; } } return -1; } /* enumerates the array. DO NOT free the name and value results * * set beggining to TRUE to start over at the beginning */ PUBLIC void PC_EnumerateNameValueArray(PCNameValueArray *array, char **name, char **value, XP_Bool beginning) { *name = NULL; *value = NULL; if(!array) return; if(beginning) { array->cur_ptr = 0; } else if(array->cur_ptr >= array->first_empty) { return; } *name = array->pairs[array->cur_ptr].name; *value = array->pairs[array->cur_ptr].value; array->cur_ptr++; return; } /* private ver. takes pre malloced name and value strings */ PRIVATE int pc_add_to_namevaluearray(PCNameValueArray *array, char *name, char *value) { if(array) { if(array->first_empty >= array->size-1) { /* need to grow */ array->size += GROW_ARRAY_BY; array->pairs = (PCNameValuePair *)XP_REALLOC(array->pairs, array->size * sizeof(PCNameValuePair)); } if(!array->pairs) { array->size = 0; return -1; } array->pairs[array->first_empty].name = name; array->pairs[array->first_empty].value = value; array->first_empty++; if(!array->pairs[array->first_empty-1].name || !array->pairs[array->first_empty-1].value) return -1; return 0; } return -1; } /* adds to end of name value array * * Possible to add duplicate names with this */ PUBLIC int PC_AddToNameValueArray(PCNameValueArray *array, char *name, char *value) { char *m_name = XP_STRDUP(name); char *m_value = XP_STRDUP(value); if(!m_name || !m_value) { XP_FREEIF(m_name); XP_FREEIF(m_value); return -1; } return pc_add_to_namevaluearray(array, name, value); } /* takes a key string as input and returns a char * pointer * in data to the serialized data structure previously stored or NULL. * len will be filled in to the length of the data string * * A module name is also passed in to guarentee that a key from * another module is never returned by an accidental key match. */ PUBLIC void PC_CheckForStoredPasswordData(char *module, char *key, char **data, int32 *len) { DBT k_key, k_data; char *combo; int status; *len = 0; *data = NULL; if(!pc_open_database()) return; if((combo = pc_gen_key(module, key)) == NULL) return; k_key.size = XP_STRLEN(combo); k_key.data = combo; status = (*pw_database->get)(pw_database, &k_key, &k_data, 0); XP_FREE(combo); if(status != 0) return; *data = k_data.data; *len = k_data.size; return; } /* returns 0 on success else -1 */ PUBLIC int PC_DeleteStoredPassword(char *module, char *key) { DBT k_key; char *combo; int status; if(!pc_open_database()) return -1; if((combo = pc_gen_key(module, key)) == NULL) return -1; k_key.size = XP_STRLEN(combo); k_key.data = combo; status = (*pw_database->del)(pw_database, &k_key, 0); XP_FREE(combo); if(status != 0) return -1; return 0; } /* takes a key string as input and returns a name value array * * A module name is also passed in to guarentee that a key from * another module is never returned by an accidental key match. */ PUBLIC PCNameValueArray * PC_CheckForStoredPasswordArray(char *module, char *key) { char *data; int32 len; PC_CheckForStoredPasswordData(module, key, &data, &len); if(!data) return NULL; return PC_CharToNameValueArray(data, len); } /* stores a serialized data stream in the password database * returns 0 on success */ PUBLIC int PC_StoreSerializedPassword(char *module, char *key, char *data, int32 len) { char *combo; DBT k_key, k_data; int status; if(!pc_open_database()) return 0; if((combo = pc_gen_key(module, key)) == NULL) return -1; k_key.size = XP_STRLEN(combo)+1; k_key.data = combo; k_data.data = data; k_data.size = len; status = (*pw_database->put)(pw_database, &k_key, &k_data, 0); XP_FREE(combo); if(status != 0) return -1; status = (*pw_database->sync)(pw_database, 0); return 0; } /* stores a name value array in the password database * returns 0 on success */ PUBLIC int PC_StorePasswordNameValueArray(char *module, char *key, PCNameValueArray *array) { char *data; int32 len; int status; PC_SerializeNameValueArray(array, &data, &len); if(!data) return -1; status = PC_StoreSerializedPassword(module, key, data, len); XP_FREE(data); return status; } #define SERIALIZER_VERSION_NUM 1 /* takes a name value array and serializes to a char string. * string is returned in "data" with length "len" * * data will always be NULL on error */ PUBLIC void PC_SerializeNameValueArray(PCNameValueArray *array, char **data, int32 *len) { int32 total_size, net_long; char *cur_ptr; char *name, *value; *len = 0; *data = NULL; XP_ASSERT(array && array->pairs); if(!array) return; total_size = sizeof(int32)*3; /* start with checksum + ver + array_size amount */ /* determine size of arrays */ PC_EnumerateNameValueArray(array, &name, &value, TRUE); while(name) { total_size += sizeof(int32); /* size of name len */ total_size += XP_STRLEN(name)+1; total_size += sizeof(int32); /* size of value len */ if(value) total_size += XP_STRLEN(value)+1; PC_EnumerateNameValueArray(array, &name, &value, FALSE); } /* malloc enough space */ *data = XP_ALLOC(sizeof(char) * total_size); if(!*data) return; cur_ptr = *data; net_long = PR_htonl(total_size); XP_MEMCPY(cur_ptr, &net_long, sizeof(int32)); cur_ptr += sizeof(int32); net_long = PR_htonl(SERIALIZER_VERSION_NUM); XP_MEMCPY(cur_ptr, &net_long, sizeof(int32)); cur_ptr += sizeof(int32); net_long = PR_htonl(PC_ArraySize(array)); XP_MEMCPY(cur_ptr, &net_long, sizeof(int32)); cur_ptr += sizeof(int32); PC_EnumerateNameValueArray(array, &name, &value, TRUE); while(name) { net_long = PR_htonl((name ? XP_STRLEN(name)+1 : 0)); XP_MEMCPY(cur_ptr, &net_long, sizeof(int32)); cur_ptr += sizeof(int32); net_long = PR_ntohl(net_long); /* convert back to true len */ if(net_long) XP_MEMCPY((void *)cur_ptr, name, net_long); cur_ptr += net_long; net_long = PR_htonl((value ? XP_STRLEN(value)+1 : 0)); XP_MEMCPY(cur_ptr, &net_long, sizeof(int32)); cur_ptr += sizeof(int32); net_long = PR_ntohl(net_long); /* convert back to true len */ if(net_long) XP_MEMCPY((void *)cur_ptr, value, net_long); cur_ptr += net_long; PC_EnumerateNameValueArray(array, &name, &value, FALSE); } *len = total_size; return; } /* returns a PCNameValueArray from serialized char data. * * returns NULL on error */ PUBLIC PCNameValueArray * PC_CharToNameValueArray(char *data, int32 len) { int32 host_long, str_len, len_read, array_size, index; char *cur_ptr; char *name, *value; PCNameValueArray *array; /* must be at least 12 bytes, len and ver and array_size */ XP_ASSERT(len >= 12); if(len < 12) return NULL; cur_ptr = data; /* read first 4 bytes as checksum */ XP_MEMCPY(&host_long, cur_ptr, 4); host_long = PR_ntohl(host_long); cur_ptr += sizeof(int32); len_read = sizeof(int32); if(host_long != len) { XP_ASSERT(0); /* this one might happen on db error */ return NULL; /* failed checksum */ } /* read next 4 bytes as ver */ XP_MEMCPY(&host_long, cur_ptr, 4); host_long = PR_ntohl(host_long); cur_ptr += sizeof(int32); len_read += sizeof(int32); if(host_long != SERIALIZER_VERSION_NUM) { XP_ASSERT(0); return NULL; /* failed ver check */ } /* read next 4 bytes as array_size */ XP_MEMCPY(&array_size, cur_ptr, 4); array_size = PR_ntohl(array_size); cur_ptr += sizeof(int32); len_read += sizeof(int32); /* malloc arrays */ array = pc_new_namevaluearray(array_size); if(!array) return NULL; index = 0; while(len_read < len) { /* next 4 bytes is length of name string */ XP_MEMCPY(&str_len, cur_ptr, 4); str_len = PR_ntohl(str_len); cur_ptr += sizeof(int32); len_read += sizeof(int32); if(len_read + str_len > len) goto error_out; name = XP_ALLOC(str_len * sizeof(char)); if(!name) goto error_out; XP_MEMCPY(name, cur_ptr, str_len); len_read += str_len; cur_ptr += str_len; if(len_read >= len) goto error_out; /* next 4 bytes is length of value string */ XP_MEMCPY(&str_len, cur_ptr, 4); str_len = PR_ntohl(str_len); cur_ptr += sizeof(int32); len_read += sizeof(int32); if(len_read + str_len > len) goto error_out; value = XP_ALLOC(str_len * sizeof(char)); if(!value) goto error_out; XP_MEMCPY(value, cur_ptr, str_len); len_read += str_len; cur_ptr += str_len; pc_add_to_namevaluearray(array, name, value); index++; } XP_ASSERT(len_read == len); return array; error_out: XP_ASSERT(0); PC_FreeNameValueArray(array); return NULL; }