diff --git a/forms/gpgol-cs.cfg b/forms/gpgol-cs.cfg index 11d7471..59ff43a 100644 --- a/forms/gpgol-cs.cfg +++ b/forms/gpgol-cs.cfg @@ -1,34 +1,34 @@ [Description] MessageClass=IPM.Note.GpgOL.ClearSigned DesignerRuntimeGuid={0006F020-0000-0000-C000-000000000046} CLSID={00061033-0000-0000-C000-000000000046} DisplayName=Form for class IPM.Note.GpgOL.MultipartSigned Category=Standard Subcategory=Formular Comment= LargeIcon=sign-l.ico -SmallIcon=sign-s.ico +SmallIcon=sign-s-smime.ico VersionMajor=1 VersionMinor=0 Hidden=1 Owner=Public Domain [Properties] [Verbs] Verb1=1 [Verb.1] DisplayName=&Open Code=0 Flags=0 Attribs=2 [Extensions] Extensions1=1 [Extension.1] Type=30 NmidPropset={00020D0C-0000-0000-C000-000000000046} NmidInteger=1 Value=1011111111111111 diff --git a/forms/gpgol-form-encrypted.cfg b/forms/gpgol-form-encrypted.cfg index 12f7140..42e3dc8 100644 --- a/forms/gpgol-form-encrypted.cfg +++ b/forms/gpgol-form-encrypted.cfg @@ -1,34 +1,34 @@ [Description] MessageClass=IPM.Note.InfoPathForm.GpgOL.SMIME.MultipartSigned DesignerRuntimeGuid={0006F020-0000-0000-C000-000000000046} CLSID={00061033-0000-0000-C000-000000000046} DisplayName=Form for GpgOL sent encrypted Mails Category=Standard Subcategory=Formular Comment= LargeIcon=encr-l.ico -SmallIcon=encr-s.ico +SmallIcon=encr-s-smime.ico VersionMajor=1 VersionMinor=0 Hidden=1 Owner=Public Domain [Properties] [Verbs] Verb1=1 [Verb.1] DisplayName=&Open Code=0 Flags=0 Attribs=2 [Extensions] Extensions1=1 [Extension.1] Type=30 NmidPropset={00020D0C-0000-0000-C000-000000000046} NmidInteger=1 Value=1011111111111111 diff --git a/forms/gpgol-form-signed-smime-opaque.cfg b/forms/gpgol-form-signed-smime-opaque.cfg deleted file mode 100644 index bcff769..0000000 --- a/forms/gpgol-form-signed-smime-opaque.cfg +++ /dev/null @@ -1,34 +0,0 @@ -[Description] -MessageClass=IPM.Note.GpgOL.OpaqueSigned -DesignerRuntimeGuid={0006F020-0000-0000-C000-000000000046} -CLSID={00061033-0000-0000-C000-000000000046} -DisplayName=Form for signed S/MIME Mails in GpgOL -Category=Standard -Subcategory=Formular -Comment= -LargeIcon=sign-l.ico -SmallIcon=sign-s-smime.ico -VersionMajor=1 -VersionMinor=0 -Hidden=1 -Owner=Public Domain - -[Properties] - -[Verbs] -Verb1=1 - -[Verb.1] -DisplayName=&Open -Code=0 -Flags=0 -Attribs=2 - -[Extensions] -Extensions1=1 - -[Extension.1] -Type=30 -NmidPropset={00020D0C-0000-0000-C000-000000000046} -NmidInteger=1 -Value=1011111111111111 diff --git a/forms/gpgol-form-signed-smime.cfg b/forms/gpgol-form-signed-smime.cfg deleted file mode 100644 index 8dd76ad..0000000 --- a/forms/gpgol-form-signed-smime.cfg +++ /dev/null @@ -1,34 +0,0 @@ -[Description] -MessageClass=IPM.Note.GpgOL.SM.MultipartSigned -DesignerRuntimeGuid={0006F020-0000-0000-C000-000000000046} -CLSID={00061033-0000-0000-C000-000000000046} -DisplayName=Form for signed S/MIME Mails in GpgOL -Category=Standard -Subcategory=Formular -Comment= -LargeIcon=sign-l.ico -SmallIcon=sign-s-smime.ico -VersionMajor=1 -VersionMinor=0 -Hidden=1 -Owner=Public Domain - -[Properties] - -[Verbs] -Verb1=1 - -[Verb.1] -DisplayName=&Open -Code=0 -Flags=0 -Attribs=2 - -[Extensions] -Extensions1=1 - -[Extension.1] -Type=30 -NmidPropset={00020D0C-0000-0000-C000-000000000046} -NmidInteger=1 -Value=1011111111111111 diff --git a/forms/gpgol-form-signed.cfg b/forms/gpgol-form-signed.cfg index acb562d..e607f74 100644 --- a/forms/gpgol-form-signed.cfg +++ b/forms/gpgol-form-signed.cfg @@ -1,34 +1,34 @@ [Description] MessageClass=IPM.Note.InfoPathForm.GpgOLS.SMIME.MultipartSigned DesignerRuntimeGuid={0006F020-0000-0000-C000-000000000046} CLSID={00061033-0000-0000-C000-000000000046} DisplayName=Form for GpgOL sent signed Mails Category=Standard Subcategory=Formular Comment= LargeIcon=sign-l.ico -SmallIcon=sign-s.ico +SmallIcon=sign-s-smime.ico VersionMajor=1 VersionMinor=0 Hidden=1 Owner=Public Domain [Properties] [Verbs] Verb1=1 [Verb.1] DisplayName=&Open Code=0 Flags=0 Attribs=2 [Extensions] Extensions1=1 [Extension.1] Type=30 NmidPropset={00020D0C-0000-0000-C000-000000000046} NmidInteger=1 Value=1011111111111111 diff --git a/forms/gpgol-ms.cfg b/forms/gpgol-ms.cfg index 10b47c5..74c4935 100644 --- a/forms/gpgol-ms.cfg +++ b/forms/gpgol-ms.cfg @@ -1,34 +1,34 @@ [Description] MessageClass=IPM.Note.GpgOL.MultipartSigned DesignerRuntimeGuid={0006F020-0000-0000-C000-000000000046} CLSID={00061033-0000-0000-C000-000000000046} DisplayName=Form for class IPM.Note.GpgOL.MultipartSigned Category=Standard Subcategory=Formular Comment= LargeIcon=sign-l.ico -SmallIcon=sign-s.ico +SmallIcon=sign-s-smime.ico VersionMajor=1 VersionMinor=0 Hidden=1 Owner=Public Domain [Properties] [Verbs] Verb1=1 [Verb.1] DisplayName=&Open Code=0 Flags=0 Attribs=2 [Extensions] Extensions1=1 [Extension.1] Type=30 NmidPropset={00020D0C-0000-0000-C000-000000000046} NmidInteger=1 Value=1011111111111111 diff --git a/forms/gpgol.cfg b/forms/gpgol.cfg index 08a72f7..827735b 100644 --- a/forms/gpgol.cfg +++ b/forms/gpgol.cfg @@ -1,34 +1,34 @@ [Description] MessageClass=IPM.Note.GpgOL DesignerRuntimeGuid={0006F020-0000-0000-C000-000000000046} CLSID={00061033-0000-0000-C000-000000000046} DisplayName=Form for class IPM.Note.GpgOL Category=Standard Subcategory=Formular Comment= LargeIcon=encr-l.ico -SmallIcon=encr-s.ico +SmallIcon=encr-s-smime.ico VersionMajor=1 VersionMinor=0 Hidden=1 Owner=Public Domain [Properties] [Verbs] Verb1=1 [Verb.1] DisplayName=&Open Code=0 Flags=0 Attribs=2 [Extensions] Extensions1=1 [Extension.1] Type=30 NmidPropset={00020D0C-0000-0000-C000-000000000046} NmidInteger=1 Value=1011111111111111 diff --git a/src/common.cpp b/src/common.cpp index 4162324..2818b58 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -1,871 +1,872 @@ /* common.c - Common routines used by GpgOL * Copyright (C) 2005, 2007, 2008 g10 Code GmbH * 2015, 2016, 2017 Bundesamt für Sicherheit in der Informationstechnik * Software engineering by Intevation GmbH + * 2020 g10 Code GmbH * * This file is part of GpgOL. * * GpgOL 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. * * GpgOL 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 Lesser General Public License * along with this program; if not, see . */ #include #define OEMRESOURCE /* Required for OBM_CHECKBOXES. */ #include #include #ifndef CSIDL_APPDATA #define CSIDL_APPDATA 0x001a #endif #ifndef CSIDL_LOCAL_APPDATA #define CSIDL_LOCAL_APPDATA 0x001c #endif #ifndef CSIDL_FLAG_CREATE #define CSIDL_FLAG_CREATE 0x8000 #endif #include #include #include #include "common.h" #include "dialogs.h" #include HINSTANCE glob_hinst = NULL; void set_global_hinstance (HINSTANCE hinst) { glob_hinst = hinst; } void bring_to_front (HWND wid) { if (wid) { if (!SetForegroundWindow (wid)) { log_debug ("%s:%s: SetForegroundWindow failed", SRCNAME, __func__); /* Yet another fallback which will not work on some * versions and is not recommended by msdn */ if (!ShowWindow (wid, SW_SHOWNORMAL)) { log_debug ("%s:%s: ShowWindow failed.", SRCNAME, __func__); } } } log_debug ("%s:%s: done", SRCNAME, __func__); } void fatal_error (const char *format, ...) { va_list arg_ptr; char buf[512]; va_start (arg_ptr, format); vsnprintf (buf, sizeof buf -1, format, arg_ptr); buf[sizeof buf - 1] = 0; va_end (arg_ptr); MessageBox (NULL, buf, "Fatal Error", MB_OK); abort (); } /* Helper for read_w32_registry_string(). */ static HKEY get_root_key(const char *root) { HKEY root_key; if( !root ) root_key = HKEY_CURRENT_USER; else if( !strcmp( root, "HKEY_CLASSES_ROOT" ) ) root_key = HKEY_CLASSES_ROOT; else if( !strcmp( root, "HKEY_CURRENT_USER" ) ) root_key = HKEY_CURRENT_USER; else if( !strcmp( root, "HKEY_LOCAL_MACHINE" ) ) root_key = HKEY_LOCAL_MACHINE; else if( !strcmp( root, "HKEY_USERS" ) ) root_key = HKEY_USERS; else if( !strcmp( root, "HKEY_PERFORMANCE_DATA" ) ) root_key = HKEY_PERFORMANCE_DATA; else if( !strcmp( root, "HKEY_CURRENT_CONFIG" ) ) root_key = HKEY_CURRENT_CONFIG; else return NULL; return root_key; } #if defined(_WIN64) #define CROSS_ACCESS KEY_WOW64_32KEY #else #define CROSS_ACCESS KEY_WOW64_64KEY #endif std::string _readRegStr (HKEY root_key, const char *dir, const char *name, bool alternate) { #ifndef _WIN32 (void) root_key; (void)alternate; (void)dir; (void)name; return std::string(); #else HKEY key_handle; DWORD n1, nbytes, type; std::string ret; DWORD flags = KEY_READ; if (alternate) { flags |= CROSS_ACCESS; } if (RegOpenKeyExA(root_key, dir, 0, flags, &key_handle)) { return ret; } nbytes = 1; if (RegQueryValueExA(key_handle, name, 0, nullptr, nullptr, &nbytes)) { RegCloseKey (key_handle); return ret; } n1 = nbytes+1; char result[n1]; if (RegQueryValueExA(key_handle, name, 0, &type, (LPBYTE)result, &n1)) { RegCloseKey(key_handle); return ret; } RegCloseKey(key_handle); result[nbytes] = 0; /* make sure it is really a string */ ret = result; if (type == REG_EXPAND_SZ && strchr (result, '%')) { n1 += 1000; char tmp[n1 +1]; nbytes = ExpandEnvironmentStringsA(ret.c_str(), tmp, n1); if (nbytes && nbytes > n1) { n1 = nbytes; char tmp2[n1 +1]; nbytes = ExpandEnvironmentStringsA(result, tmp2, n1); if (nbytes && nbytes > n1) { /* oops - truncated, better don't expand at all */ return ret; } tmp2[nbytes] = 0; ret = tmp2; } else if (nbytes) { /* okay, reduce the length */ tmp[nbytes] = 0; ret = tmp; } } return ret; #endif } std::string readRegStr (const char *root, const char *dir, const char *name) { #ifndef _WIN32 (void)root; (void)dir; (void)name; return std::string(); #else HKEY root_key; std::string ret; if (!(root_key = get_root_key(root))) { return ret; } ret = _readRegStr (root_key, dir, name, false); if (ret.empty()) { // Try alternate as fallback ret = _readRegStr (root_key, dir, name, true); } if (ret.empty()) { // Try local machine as fallback. ret = _readRegStr (HKEY_LOCAL_MACHINE, dir, name, false); if (ret.empty()) { // Try alternative registry view as fallback ret = _readRegStr (HKEY_LOCAL_MACHINE, dir, name, true); } } return ret; #endif } /* Return a string from the Win32 Registry or NULL in case of error. Caller must release the return value. A NULL for root is an alias for HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE in turn. NOTE: The value is allocated with a plain xmalloc () - use xfree () and not the usual xfree(). */ char * read_w32_registry_string (const char *root, const char *dir, const char *name) { const auto ret = readRegStr (root, dir, name); if (ret.empty()) { return nullptr; } return xstrdup (ret.c_str ()); } /* Return the data dir used for forms etc. Returns NULL on error. */ char * get_data_dir (void) { char *instdir; char *p; char *dname; instdir = get_gpg4win_dir(); if (!instdir) return NULL; /* Build the key: "/share/gpgol". */ #define SDDIR "\\share\\gpgol" dname = (char*) xmalloc (strlen (instdir) + strlen (SDDIR) + 1); if (!dname) { xfree (instdir); return NULL; } p = dname; strcpy (p, instdir); p += strlen (instdir); strcpy (p, SDDIR); xfree (instdir); #undef SDDIR return dname; } /* Percent-escape the string STR by replacing colons with '%3a'. If EXTRA is not NULL all characters in it are also escaped. */ char * percent_escape (const char *str, const char *extra) { int i, j; char *ptr; if (!str) return NULL; for (i=j=0; str[i]; i++) if (str[i] == ':' || str[i] == '%' || (extra && strchr (extra, str[i]))) j++; ptr = (char *) xmalloc (i + 2 * j + 1); i = 0; while (*str) { /* FIXME: Work around a bug in Kleo. */ if (*str == ':') { ptr[i++] = '%'; ptr[i++] = '3'; ptr[i++] = 'a'; } else { if (*str == '%') { ptr[i++] = '%'; ptr[i++] = '2'; ptr[i++] = '5'; } else if (extra && strchr (extra, *str)) { ptr[i++] = '%'; ptr[i++] = tohex_lower ((*str >> 4) & 15); ptr[i++] = tohex_lower (*str & 15); } else ptr[i++] = *str; } str++; } ptr[i] = '\0'; return ptr; } /* Fix linebreaks. This replaces all consecutive \r or \n characters by a single \n. There can be extremly weird combinations of linebreaks like \r\r\n\r\r\n at the end of each line when getting the body of a mail message. */ void fix_linebreaks (char *str, int *len) { char *src; char *dst; src = str; dst = str; while (*src) { if (*src == '\r' || *src == '\n') { do src++; while (*src == '\r' || *src == '\n'); *(dst++) = '\n'; } else { *(dst++) = *(src++); } } *dst = '\0'; *len = dst - str; } /* Get a pretty name for the file at path path. File extension will be set to work for the protocol as provided in protocol and depends on the signature setting. Set signature to 0 if the extension should not be a signature extension. Returns NULL on success. Caller must free result. */ wchar_t * get_pretty_attachment_name (wchar_t *path, protocol_t protocol, int signature) { wchar_t* pretty; wchar_t* buf; if (!path || !wcslen (path)) { log_error("%s:%s: No path given", SRCNAME, __func__); return NULL; } pretty = (wchar_t*) xmalloc ((MAX_PATH + 1) * sizeof (wchar_t)); memset (pretty, 0, (MAX_PATH + 1) * sizeof (wchar_t)); buf = wcsrchr (path, '\\') + 1; if (!buf || !*buf) { log_error("%s:%s: No filename found in path", SRCNAME, __func__); xfree (pretty); return NULL; } wcscpy (pretty, buf); buf = pretty + wcslen(pretty); if (signature) { if (protocol == PROTOCOL_SMIME) { *(buf++) = '.'; *(buf++) = 'p'; *(buf++) = '7'; *(buf++) = 's'; } else { *(buf++) = '.'; *(buf++) = 's'; *(buf++) = 'i'; *(buf++) = 'g'; } } else { if (protocol == PROTOCOL_SMIME) { *(buf++) = '.'; *(buf++) = 'p'; *(buf++) = '7'; *(buf++) = 'm'; } else { *(buf++) = '.'; *(buf++) = 'g'; *(buf++) = 'p'; *(buf++) = 'g'; } } return pretty; } static HANDLE CreateFileUtf8 (const char *utf8Name) { if (!utf8Name) { return INVALID_HANDLE_VALUE; } wchar_t *wname = utf8_to_wchar (utf8Name); if (!wname) { TRACEPOINT; return INVALID_HANDLE_VALUE; } auto ret = CreateFileW (wname, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY, NULL); xfree (wname); return ret; } static std::string getTmpPathUtf8 () { static std::string ret; if (!ret.empty()) { return ret; } wchar_t tmpPath[MAX_PATH + 2]; if (!GetTempPathW (MAX_PATH, tmpPath)) { log_error ("%s:%s: Could not get tmp path.", SRCNAME, __func__); return ret; } char *utf8Name = wchar_to_utf8 (tmpPath); if (!utf8Name) { TRACEPOINT; return ret; } ret = utf8Name; xfree (utf8Name); return ret; } /* Open a file in a temporary directory, take name as a suggestion and put the open Handle in outHandle. Returns the actually used file name in case there were other files with that name. */ wchar_t* get_tmp_outfile (const wchar_t *name, HANDLE *outHandle) { const auto utf8Name = wchar_to_utf8_string (name); const auto tmpPath = getTmpPathUtf8 (); if (utf8Name.empty() || tmpPath.empty()) { TRACEPOINT; return nullptr; } auto outName = tmpPath + utf8Name; log_data("%s:%s: Attachment candidate is %s", SRCNAME, __func__, outName.c_str ()); int tries = 1; while ((*outHandle = CreateFileUtf8 (outName.c_str ())) == INVALID_HANDLE_VALUE) { log_debug_w32 (-1, "%s:%s: Failed to open candidate '%s'", SRCNAME, __func__, anonstr (outName.c_str())); char *outNameC = xstrdup (outName.c_str()); const auto lastBackslash = strrchr (outNameC, '\\'); if (!lastBackslash) { /* This is an error because tmp name by definition contains one */ log_error ("%s:%s: No backslash in origname '%s'", SRCNAME, __func__, outNameC); xfree (outNameC); return NULL; } auto fileExt = strchr (lastBackslash, '.'); if (fileExt) { *fileExt = '\0'; ++fileExt; } // OutNameC is now without an extension and if // there is a file ext it now points to the extension. outName = outNameC + std::string("_") + std::to_string(tries++); if (fileExt) { outName += std::string(".") + fileExt; } xfree (outNameC); if (tries == 50) { /* Mmh fishy, maybe the name cannot be created on the file system. Let's switch to a generic name. */ log_dbg ("Can't find an attachment name. " "Switching over to a generic attachment name."); outName = tmpPath + "attachment"; if (fileExt) { outName += "."; outName += fileExt; } } if (tries > 100) { /* You have to know when to give up,.. */ log_error ("%s:%s: Could not get a name out of 100 tries", SRCNAME, __func__); return NULL; } } return utf8_to_wchar (outName.c_str ()); } /** Get the Gpg4win Install directory. * * Looks first for the Gpg4win 3.x registry key. Then for the Gpg4win * 2.x registry key. And checks that the directory can be read. * * @returns NULL if no dir could be found. Otherwise a malloced string. */ char * get_gpg4win_dir() { const char *g4win_keys[] = {GPG4WIN_REGKEY_3, GPG4WIN_REGKEY_2, NULL}; const char **key; for (key = g4win_keys; *key; key++) { char *tmp = read_w32_registry_string (NULL, *key, "Install Directory"); if (!tmp) { continue; } if (!access(tmp, R_OK)) { return tmp; } else { log_debug ("Failed to access: %s\n", tmp); xfree (tmp); } } return NULL; } static void epoch_to_file_time (unsigned long time, LPFILETIME pft) { LONGLONG ll; ll = Int32x32To64(time, 10000000) + 116444736000000000; pft->dwLowDateTime = (DWORD)ll; pft->dwHighDateTime = ll >> 32; } char * format_date_from_gpgme (unsigned long time) { wchar_t buf[256]; FILETIME ft; SYSTEMTIME st; epoch_to_file_time (time, &ft); FileTimeToSystemTime(&ft, &st); int ret = GetDateFormatEx (NULL, DATE_SHORTDATE, &st, NULL, buf, 256, NULL); if (ret == 0) { return NULL; } return wchar_to_utf8 (buf); } /* Return the name of the default UI server. This name is used to auto start an UI server if an initial connect failed. */ char * get_uiserver_name (void) { char *name = NULL; char *dir, *uiserver, *p; int extra_arglen = 9; const char * server_names[] = {"kleopatra.exe", "bin\\kleopatra.exe", "gpa.exe", "bin\\gpa.exe", NULL}; const char **tmp = NULL; dir = get_gpg4win_dir (); if (!dir) { log_error ("Failed to find gpg4win dir"); return NULL; } uiserver = read_w32_registry_string (NULL, GPG4WIN_REGKEY_3, "UI Server"); if (!uiserver) { uiserver = read_w32_registry_string (NULL, GPG4WIN_REGKEY_2, "UI Server"); } if (uiserver) { name = (char*) xmalloc (strlen (dir) + strlen (uiserver) + extra_arglen + 2); strcpy (stpcpy (stpcpy (name, dir), "\\"), uiserver); for (p = name; *p; p++) if (*p == '/') *p = '\\'; xfree (uiserver); } if (name && !access (name, F_OK)) { /* Set through registry and is accessible */ xfree(dir); return name; } /* Fallbacks */ for (tmp = server_names; *tmp; tmp++) { if (name) { xfree (name); } name = (char *) xmalloc (strlen (dir) + strlen (*tmp) + extra_arglen + 2); strcpy (stpcpy (stpcpy (name, dir), "\\"), *tmp); for (p = name; *p; p++) if (*p == '/') *p = '\\'; if (!access (name, F_OK)) { /* Found a viable candidate */ if (strstr (name, "kleopatra.exe")) { strcat (name, " --daemon"); } xfree (dir); return name; } } xfree (dir); log_error ("Failed to find a viable UIServer"); return NULL; } int has_high_integrity(HANDLE hToken) { PTOKEN_MANDATORY_LABEL integrity_label = NULL; DWORD integrity_level = 0, size = 0; if (hToken == NULL || hToken == INVALID_HANDLE_VALUE) { log_debug ("Invalid parameters."); return 0; } /* Get the required size */ if (!GetTokenInformation (hToken, TokenIntegrityLevel, NULL, 0, &size)) { if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { log_debug ("Failed to get required size.\n"); return 0; } } integrity_label = (PTOKEN_MANDATORY_LABEL) LocalAlloc(0, size); if (integrity_label == NULL) { log_debug ("Failed to allocate label. \n"); return 0; } if (!GetTokenInformation (hToken, TokenIntegrityLevel, integrity_label, size, &size)) { log_debug ("Failed to get integrity level.\n"); LocalFree(integrity_label); return 0; } /* Get the last integrity level */ integrity_level = *GetSidSubAuthority(integrity_label->Label.Sid, (DWORD)(UCHAR)(*GetSidSubAuthorityCount( integrity_label->Label.Sid) - 1)); LocalFree (integrity_label); return integrity_level >= SECURITY_MANDATORY_HIGH_RID; } int is_elevated() { int ret = 0; HANDLE hToken = NULL; if (OpenProcessToken (GetCurrentProcess(), TOKEN_QUERY, &hToken)) { DWORD elevation; DWORD cbSize = sizeof (DWORD); if (GetTokenInformation (hToken, TokenElevation, &elevation, sizeof (TokenElevation), &cbSize)) { ret = elevation; } } /* Elevation will be true and ElevationType TokenElevationTypeFull even if the token is a user token created by SAFER so we additionally check the integrity level of the token which will only be high in the real elevated process and medium otherwise. */ ret = ret && has_high_integrity (hToken); if (hToken) CloseHandle (hToken); return ret; } int gpgol_message_box (HWND parent, const char *utf8_text, const char *utf8_caption, UINT type) { wchar_t *w_text = utf8_to_wchar (utf8_text); wchar_t *w_caption = utf8_to_wchar (utf8_caption); int ret = 0; MSGBOXPARAMSW mbp; mbp.cbSize = sizeof (MSGBOXPARAMS); mbp.hwndOwner = parent; mbp.hInstance = glob_hinst; mbp.lpszText = w_text; mbp.lpszCaption = w_caption; mbp.dwStyle = type | MB_USERICON; mbp.dwLanguageId = MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT); mbp.lpfnMsgBoxCallback = NULL; mbp.dwContextHelpId = 0; mbp.lpszIcon = (LPCWSTR) MAKEINTRESOURCE (IDI_GPGOL_LOCK_ICON); ret = MessageBoxIndirectW (&mbp); xfree (w_text); xfree (w_caption); return ret; } void gpgol_bug (HWND parent, int code) { const char *bugmsg = utf8_gettext ("Operation failed.\n\n" "This is usually caused by a bug in GpgOL or an error in your setup.\n" "Please see https://www.gpg4win.org/reporting-bugs.html " "or ask your Administrator for support."); char *with_code; gpgrt_asprintf (&with_code, "%s\nCode: %i", bugmsg, code); memdbg_alloc (with_code); gpgol_message_box (parent, with_code, _("GpgOL Error"), MB_OK); xfree (with_code); return; } static int store_config_value (HKEY hk, const char *path, const char *key, const char *val) { HKEY h; int type; int ec; if (hk == NULL) { hk = HKEY_CURRENT_USER; } ec = RegCreateKeyEx (hk, path, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &h, NULL); if (ec != ERROR_SUCCESS) { log_debug_w32 (ec, "creating/opening registry key `%s' failed", path); return -1; } type = strchr (val, '%')? REG_EXPAND_SZ : REG_SZ; ec = RegSetValueEx (h, key, 0, type, (const BYTE*)val, strlen (val)); if (ec != ERROR_SUCCESS) { log_debug_w32 (ec, "saving registry key `%s'->`%s' failed", path, key); RegCloseKey(h); return -1; } RegCloseKey(h); return 0; } /* Store a key in the registry with the key given by @key and the value @value. */ int store_extension_value (const char *key, const char *val) { return store_config_value (HKEY_CURRENT_USER, GPGOL_REGPATH, key, val); } /* Load a key from the registry with the key given by @key. The value is returned in @val and needs to freed by the caller. */ int load_extension_value (const char *key, char **val) { if (!val) { STRANGEPOINT; return -1; } *val = read_w32_registry_string (nullptr, GPGOL_REGPATH, key); log_debug ("%s:%s: LoadReg '%s' val '%s'", SRCNAME, __func__, key ? key : "null", *val ? *val : "null"); return 0; } int store_extension_subkey_value (const char *subkey, const char *key, const char *val) { int ret; char *path; gpgrt_asprintf (&path, "%s\\%s", GPGOL_REGPATH, subkey); memdbg_alloc (path); ret = store_config_value (HKEY_CURRENT_USER, path, key, val); xfree (path); return ret; } diff --git a/src/olflange.cpp b/src/olflange.cpp index 9c8f779..196d7fa 100644 --- a/src/olflange.cpp +++ b/src/olflange.cpp @@ -1,439 +1,449 @@ /* olflange.cpp - Connect GpgOL to Outlook * Copyright (C) 2001 G Data Software AG, http://www.gdata.de * Copyright (C) 2004, 2005, 2007, 2008 g10 Code GmbH * * This file is part of GpgOL. * * GpgOL 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. * * GpgOL 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 program; if not, see . */ #ifdef HAVE_CONFIG_H #include #endif #include #ifndef INITGUID /* Include every header that defines a GUID below this macro. Otherwise the GUID's will only be declared and not defined. */ #define INITGUID #endif #include #include "mymapi.h" #include "mymapitags.h" #include "common.h" #include "mapihelp.h" #include "olflange.h" #include "gpgoladdin.h" static char *olversion; EXTERN_C int get_ol_main_version (void) { return olversion? atoi (olversion): 0; } /* Registers this as an addin for outlook 2010. This basically updates some Registry entries. Documentation to be found at: http://msdn.microsoft.com/en-us/library/bb386106%28v=vs.110%29.aspx */ STDAPI DllRegisterServer (void) { HKEY hkey, hkey2; CHAR szKeyBuf[MAX_PATH+1024]; CHAR szEntry[MAX_PATH+512]; TCHAR szModuleFileName[MAX_PATH]; DWORD dwTemp = 0; long ec; HKEY root_key; int inst_global = is_elevated (); if (inst_global) { root_key = HKEY_LOCAL_MACHINE; } else { root_key = HKEY_CURRENT_USER; } /* Get server location. */ if (!GetModuleFileName(glob_hinst, szModuleFileName, MAX_PATH)) return E_FAIL; hkey = NULL; lstrcpy (szKeyBuf, "Software\\GNU\\GpgOL"); RegCreateKeyEx (HKEY_CURRENT_USER, szKeyBuf, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL); if (hkey != NULL) RegCloseKey (hkey); /* Register the CLSID in the registry */ hkey = NULL; if (inst_global) { strcpy (szKeyBuf, "CLSID\\" CLSIDSTR_GPGOL); ec = RegCreateKeyEx (HKEY_CLASSES_ROOT, szKeyBuf, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL); OutputDebugString("Created: "); OutputDebugString(szKeyBuf); } else { strcpy (szKeyBuf, "Software\\Classes\\CLSID\\" CLSIDSTR_GPGOL); ec = RegCreateKeyEx (HKEY_CURRENT_USER, szKeyBuf, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL); } if (ec != ERROR_SUCCESS) { fprintf (stderr, "creating key `%s' failed: ec=%#lx\n", szKeyBuf, ec); return E_ACCESSDENIED; } strcpy (szEntry, GPGOL_PRETTY); dwTemp = strlen (szEntry) + 1; RegSetValueEx (hkey, NULL, 0, REG_SZ, (BYTE*)szEntry, dwTemp); /* Set the Inproc server value */ strcpy (szKeyBuf, "InprocServer32"); ec = RegCreateKeyEx (hkey, szKeyBuf, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey2, NULL); if (ec != ERROR_SUCCESS) { fprintf (stderr, "creating key `%s' failed: ec=%#lx\n", szKeyBuf, ec); RegCloseKey (hkey); return E_ACCESSDENIED; } strcpy (szEntry, szModuleFileName); dwTemp = strlen (szEntry) + 1; RegSetValueEx (hkey2, NULL, 0, REG_SZ, (BYTE*)szEntry, dwTemp); /* Set the threading model used */ strcpy (szEntry, "Both"); dwTemp = strlen (szEntry) + 1; RegSetValueEx (hkey2, "ThreadingModel", 0, REG_SZ, (BYTE*)szEntry, dwTemp); /* Set the Prog ID */ strcpy (szKeyBuf, "ProgID"); ec = RegCreateKeyEx (hkey, szKeyBuf, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey2, NULL); if (ec != ERROR_SUCCESS) { fprintf (stderr, "creating key `%s' failed: ec=%#lx\n", szKeyBuf, ec); RegCloseKey (hkey); return E_ACCESSDENIED; } strcpy (szEntry, GPGOL_PROGID); dwTemp = strlen (szEntry) + 1; RegSetValueEx (hkey2, NULL, 0, REG_SZ, (BYTE*)szEntry, dwTemp); /* Make the Prog ID known. This is basically the same as above * but necessary so we can refer to the Prog ID as an Outlook * Extension */ hkey = NULL; if (inst_global) { strcpy (szKeyBuf, GPGOL_PROGID); ec = RegCreateKeyEx (HKEY_CLASSES_ROOT, szKeyBuf, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL); } else { strcpy (szKeyBuf, "Software\\Classes\\" GPGOL_PROGID); ec = RegCreateKeyEx (HKEY_CURRENT_USER, szKeyBuf, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL); } if (ec != ERROR_SUCCESS) { fprintf (stderr, "creating key `%s' failed: ec=%#lx\n", szKeyBuf, ec); return E_ACCESSDENIED; } strcpy (szEntry, GPGOL_PRETTY); dwTemp = strlen (szEntry) + 1; RegSetValueEx (hkey, NULL, 0, REG_SZ, (BYTE*)szEntry, dwTemp); /* Point from the Prog ID entry to the CSLID */ strcpy (szKeyBuf, "CLSID"); ec = RegCreateKeyEx (hkey, szKeyBuf, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey2, NULL); if (ec != ERROR_SUCCESS) { fprintf (stderr, "creating key `%s' failed: ec=%#lx\n", szKeyBuf, ec); RegCloseKey (hkey); return E_ACCESSDENIED; } strcpy (szEntry, CLSIDSTR_GPGOL); dwTemp = strlen (szEntry) + 1; RegSetValueEx (hkey2, NULL, 0, REG_SZ, (BYTE*)szEntry, dwTemp); /* Register ourself as an extension for outlook >= 14 */ strcpy (szKeyBuf, "Software\\Microsoft\\Office\\Outlook\\Addins\\" GPGOL_PROGID); ec = RegCreateKeyEx (root_key, szKeyBuf, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL); if (ec != ERROR_SUCCESS) { fprintf (stderr, "creating key `%s' failed: ec=%#lx\n", szKeyBuf, ec); return E_ACCESSDENIED; } /* Load connected and load Bootload */ dwTemp = 0x01 | 0x02; RegSetValueEx (hkey, "LoadBehavior", 0, REG_DWORD, (BYTE*)&dwTemp, 4); /* We are not commandline save */ dwTemp = 0; RegSetValueEx (hkey, "CommandLineSafe", 0, REG_DWORD, (BYTE*)&dwTemp, 4); /* A friendly name (visible in outlook) */ strcpy (szEntry, GPGOL_PRETTY); dwTemp = strlen (szEntry) + 1; RegSetValueEx (hkey, "FriendlyName", 0, REG_SZ, (BYTE*)szEntry, dwTemp); /* A short description (visible in outlook) */ strcpy (szEntry, GPGOL_DESCRIPTION); dwTemp = strlen (szEntry) + 1; RegSetValueEx (hkey, "Description", 0, REG_SZ, (BYTE*)szEntry, dwTemp); RegCloseKey (hkey2); RegCloseKey (hkey); log_debug ("DllRegisterServer succeeded\n"); return S_OK; } /* Unregisters this module as an Exchange extension / Addin. */ STDAPI DllUnregisterServer (void) { HKEY hkey; CHAR buf[MAX_PATH+1024]; DWORD ntemp; long res; HKEY root_key; if (is_elevated ()) { root_key = HKEY_LOCAL_MACHINE; } else { root_key = HKEY_CURRENT_USER; } /* We still unregister the old client extension code */ strcpy (buf, "Software\\Microsoft\\Exchange\\Client\\Extensions"); /* Create and open key and subkey. */ res = RegCreateKeyEx (root_key, buf, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL); if (res != ERROR_SUCCESS) { log_debug ("DllUnregisterServer: access denied.\n"); return E_ACCESSDENIED; } RegDeleteValue (hkey, "GpgOL"); /* Set outlook update flag. */ strcpy (buf, "4.0;Outxxx.dll;7;000000000000000;0000000000;OutXXX"); ntemp = strlen (buf) + 1; RegSetValueEx (hkey, "Outlook Setup Extension", 0, REG_SZ, (BYTE*) buf, ntemp); RegCloseKey (hkey); /* Delete CLSIDs. */ strcpy (buf, "CLSID\\" CLSIDSTR_GPGOL "\\InprocServer32"); RegDeleteKey (HKEY_CLASSES_ROOT, buf); strcpy (buf, "CLSID\\" CLSIDSTR_GPGOL "\\ProgID"); RegDeleteKey (HKEY_CLASSES_ROOT, buf); strcpy (buf, "CLSID\\" CLSIDSTR_GPGOL); RegDeleteKey (HKEY_CLASSES_ROOT, buf); /* Delete ProgID */ strcpy (buf, GPGOL_PROGID "\\CLSID"); RegDeleteKey (HKEY_CLASSES_ROOT, buf); strcpy (buf, GPGOL_PROGID); RegDeleteKey (HKEY_CLASSES_ROOT, buf); /* Delete Addin entry */ strcpy (buf, "Software\\Microsoft\\Office\\Outlook\\Addins\\" GPGOL_PROGID); RegDeleteKey (root_key, buf); return S_OK; } static const char* parse_version_number (const char *s, int *number) { int val = 0; if (*s == '0' && digitp (s+1)) return NULL; /* Leading zeros are not allowed. */ for (; digitp (s); s++) { val *= 10; val += *s - '0'; } *number = val; return val < 0 ? NULL : s; } static const char * parse_version_string (const char *s, int *major, int *minor, int *micro) { s = parse_version_number (s, major); if (!s || *s != '.') return NULL; s++; s = parse_version_number (s, minor); if (!s || *s != '.') return NULL; s++; s = parse_version_number (s, micro); if (!s) return NULL; return s; /* Patchlevel. */ } static const char * compare_versions (const char *my_version, const char *req_version) { int my_major, my_minor, my_micro; int rq_major, rq_minor, rq_micro; const char *my_plvl, *rq_plvl; if (!req_version) return my_version; if (!my_version) return NULL; my_plvl = parse_version_string (my_version, &my_major, &my_minor, &my_micro); if (!my_plvl) return NULL; /* Very strange: our own version is bogus. */ rq_plvl = parse_version_string(req_version, &rq_major, &rq_minor, &rq_micro); if (!rq_plvl) return NULL; /* Requested version string is invalid. */ if (my_major > rq_major || (my_major == rq_major && my_minor > rq_minor) || (my_major == rq_major && my_minor == rq_minor && my_micro > rq_micro) || (my_major == rq_major && my_minor == rq_minor && my_micro == rq_micro && strcmp( my_plvl, rq_plvl ) >= 0)) { return my_version; } return NULL; } /* Check that the the version of GpgOL is at minimum the requested one * and return GpgOL's version string; return NULL if that condition is * not met. If a NULL is passed to this function, no check is done * and the version string is simply returned. */ EXTERN_C const char * __stdcall gpgol_check_version (const char *req_version) { return compare_versions (PACKAGE_VERSION, req_version); } void install_forms (void) { HRESULT hr; LPMAPIFORMCONTAINER formcontainer = NULL; static char const *forms[] = { "gpgol", "gpgol-ms", "gpgol-cs", /* The InfoPath we use for sending, to get outlook to do the S/MIME handling. */ "gpgol-form-signed", "gpgol-form-encrypted", - /* SMIME Forms with similar icons to outlooks S/MIME */ - "gpgol-form-signed-smime", - "gpgol-form-signed-smime-opaque", - "gpgol-form-encrypted-smime", + NULL, + }; + /* A list of old forms that we had registered in the past. */ + static char const *oldClasses[] = + { + "IPM.Note.GpgOL.OpaqueSigned", + "IPM.Note.GpgOL.SM.MultipartSigned", NULL, }; int formidx; char buffer[MAX_PATH+10]; char *datadir; int any_error = 0; MAPIOpenLocalFormContainer (&formcontainer); if (!formcontainer) { log_error ("%s:%s: error getting local form container\n", SRCNAME, __func__); return; } memdbg_addRef (formcontainer); datadir = get_data_dir (); if (!datadir) { log_error ("%s:%s: error getting data directory\n", SRCNAME, __func__); gpgol_release (formcontainer); return; } for (formidx=0; forms[formidx]; formidx++) { snprintf (buffer, MAX_PATH, "%s\\%s.cfg", datadir, forms[formidx]); hr = formcontainer->InstallForm (0, MAPIFORM_INSTALL_OVERWRITEONCONFLICT, buffer); if (hr) { any_error = 1; LPMAPIERROR err; formcontainer->GetLastError (hr, 0, &err); log_error ("%s:%s: installing form `%s' failed: hr=%#lx err=%s\n", SRCNAME, __func__, buffer, hr, err ? err->lpszError : "null"); MAPIFreeBuffer (err); } else log_debug ("%s:%s: form `%s' installed\n", SRCNAME, __func__, buffer); } + for (formidx=0; oldClasses[formidx]; formidx++) + { + if (!formcontainer->RemoveForm (oldClasses[formidx])) + { + log_dbg ("Removed old message class form: %s", oldClasses[formidx]); + } + } gpgol_release (formcontainer); xfree (datadir); if (!any_error) { opt.forms_revision = GPGOL_FORMS_REVISION; write_options (); } }