From 48c2ac8c879a83c4f8737b978ca9c3232bb2e70d Mon Sep 17 00:00:00 2001 From: =?utf8?q?Rafa=C3=ABl=20Carr=C3=A9?= Date: Fri, 14 Dec 2007 17:53:27 +0000 Subject: [PATCH] Disable update checking per default, re-enable with --enable-update-check Add OpenPGP code, still unused as there is no download of binaries at the moment We will need to define (and write) the files (and their location) we will use in the update system: * We will use a file per arch, which will be signed * ALL VideoLAN gpg public keys will be stored on http://download.videolan.org/pub/keys/XXXXXXXXXXXXXXXX.asc where XXXXXXXXXXXXXXXX is the long id of the key * Every downloadable file will be signed with gpg --sign --detach --armor --- configure.ac | 14 + doc/release-howto.txt | 8 +- include/vlc_update.h | 215 ++++++- modules/control/rc.c | 10 +- modules/gui/macosx/intf.h | 2 + modules/gui/macosx/intf.m | 8 +- modules/gui/macosx/update.h | 2 + modules/gui/macosx/update.m | 3 + modules/gui/qt4/dialogs/help.cpp | 9 +- modules/gui/qt4/dialogs/help.hpp | 6 +- modules/gui/qt4/dialogs_provider.cpp | 2 + modules/gui/qt4/dialogs_provider.hpp | 2 + modules/gui/qt4/menus.cpp | 4 + modules/gui/wxwidgets/dialogs.cpp | 8 + modules/gui/wxwidgets/dialogs/updatevlc.cpp | 3 + modules/gui/wxwidgets/dialogs/updatevlc.hpp | 4 + modules/gui/wxwidgets/interface.cpp | 8 + src/misc/update.c | 661 ++++++++++++++++++++ 18 files changed, 958 insertions(+), 11 deletions(-) diff --git a/configure.ac b/configure.ac index c514ea832f..5bdd942eaf 100644 --- a/configure.ac +++ b/configure.ac @@ -5589,6 +5589,20 @@ then fi AM_CONDITIONAL([HAVE_LIBGCRYPT], [test "${have_libgcrypt}" = "yes"]) +dnl +dnl update checking system +dnl +AC_ARG_ENABLE(update-check, + [ --enable-update-check update checking system (default disabled)]) +if test "${enable_update_check}" = "yes" +then + if test "${have_libgcrypt}" != "yes" + then + AC_MSG_ERROR([libgcrypt is required for update checking system]) + fi + VLC_ADD_LIBS([libvlc], [-lgcrypt]) + AC_DEFINE([UPDATE_CHECK], [1], [Define if you want to use the VLC update mechanism]) +fi dnl dnl Endianness check, AC_C_BIGENDIAN doesn't work if we are cross-compiling diff --git a/doc/release-howto.txt b/doc/release-howto.txt index e6835764a8..ce8bea4f8d 100644 --- a/doc/release-howto.txt +++ b/doc/release-howto.txt @@ -15,6 +15,7 @@ · read all the commits and add important things to the NEWS file · update the milestones info on https://trac.videolan.org/vlc - Add a note about the matching contrib package in INSTALL.win32 + - Make sure that the gpg key embedded in include/vlc_update.h is the last one * Commit @@ -32,19 +33,20 @@ * BeOS Packages Information on building: http://developers.videolan.org/vlc/beos-compile.html + Configure with --enable-update-check Build in the "buildbeos" chroot on altair. # add the .zip files to /opt/ftp/pub/videolan/testing/vlc-X.X.X/beos/ + generate md5 hashes and gpg signature of these files * Win32 Packages - make the packages using the nightly builds configure/options/... + make the packages using the nightly builds configure/options/... , don't forget --enable-update-check don't forget to test the installer and uninstaller (the first 0.8.4 uninstaller was broken ... kind of suxxs) add the .zip and .exe files to /opt/ftp/pub/videolan/testing/vlc-X.X.X/win32/ generate md5 hashes and gpg signature of these files * OS X packages - At the moment, only FK can do them (so they can be compatible with OS X 10.2) - Later: on the G5 + configure with --enable-update-check generate md5 hashes and gpg signature of these files * Commit changes ... it never works the first time diff --git a/include/vlc_update.h b/include/vlc_update.h index f36e4059e5..cfac788f55 100644 --- a/include/vlc_update.h +++ b/include/vlc_update.h @@ -1,10 +1,11 @@ /***************************************************************************** * vlc_update.h: VLC update and plugins download ***************************************************************************** - * Copyright (C) 2005 the VideoLAN team + * Copyright © 2005-2007 the VideoLAN team * $Id$ * * Authors: Antoine Cellerier + * Rafaël Carré * * 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 @@ -25,17 +26,226 @@ #error You are not libvlc or one of its plugins. You cannot include this file #endif +#ifdef UPDATE_CHECK + #ifndef _VLC_UPDATE_H #define _VLC_UPDATE_H #include +#include /* key & signature downloading */ +#include /* b64 decoding */ +#include /* utf8_fopen() */ +#include /* cryptography and digest algorithms */ + /** * \defgroup update Update * * @{ */ +enum /* Public key algorithms */ +{ + /* we will only use DSA public keys */ + PUBLIC_KEY_ALGO_DSA = 0x11 +}; + +enum /* Digest algorithms */ +{ + /* and DSA use SHA-1 digest */ + DIGEST_ALGO_SHA1 = 0x02 +}; + +enum /* Packet types */ +{ + SIGNATURE_PACKET = 0x02, + PUBLIC_KEY_PACKET = 0x06, + USER_ID_PACKET = 0x0d +}; + +enum /* Signature types */ +{ + BINARY_SIGNATURE = 0x00, + TEXT_SIGNATURE = 0x01, + + /* Public keys signatures */ + GENERIC_KEY_SIGNATURE = 0x10, /* No assumption of verification */ + PERSONA_KEY_SIGNATURE = 0x11, /* No verification has been made */ + CASUAL_KEY_SIGNATURE = 0x12, /* Some casual verification */ + POSITIVE_KEY_SIGNATURE = 0x13 /* Substantial verification */ +}; + + +enum /* Signature subpacket types */ +{ + ISSUER_SUBPACKET = 0x10 +}; + + + +struct public_key_packet_t +{ /* a public key packet (DSA/SHA-1) is 418 bytes */ + + uint8_t version; /* we use only version 4 */ + uint8_t timestamp[4]; /* creation time of the key */ + uint8_t algo; /* we only use DSA */ + /* the multi precision integers, with their 2 bytes length header */ + uint8_t p[2+128]; + uint8_t q[2+20]; + uint8_t g[2+128]; + uint8_t y[2+128]; +}; + +/* used for public key signatures */ +struct signature_packet_v4_t +{ /* hashed_data or unhashed_data can be empty, so the signature packet is + * theorically at least 54 bytes long, but always more than that. */ + + uint8_t version; + uint8_t type; + uint8_t public_key_algo; + uint8_t digest_algo; + uint8_t hashed_data_len[2]; + uint8_t *hashed_data; + uint8_t unhashed_data_len[2]; + uint8_t *unhashed_data; + uint8_t hash_verification[2]; + + /* The part below is made of consecutive MPIs, their number and size being + * public-key-algorithm dependant. + * But since we use DSA signatures only, we fix it. */ + uint8_t r[2+20]; + uint8_t s[2+20]; +}; + +/* Used for binary document signatures (to be compatible with older software) + * DSA/SHA-1 is always 65 bytes */ +struct signature_packet_v3_t +{ + uint8_t header[2]; + uint8_t version; /* 3 */ + uint8_t hashed_data_len; /* MUST be 5 */ + uint8_t type; + uint8_t timestamp[4]; /* 4 bytes scalar number */ + uint8_t issuer_longid[8]; /* The key which signed the document */ + uint8_t public_key_algo; /* we only know about DSA */ + uint8_t digest_algo; /* and his little sister SHA-1 */ + uint8_t hash_verification[2];/* the 2 1st bytes of the SHA-1 hash */ + + /* The part below is made of consecutive MPIs, their number and size being + * public-key-algorithm dependant. + * But since we use DSA signatures only, we fix it. */ + uint8_t r[2+20]; + uint8_t s[2+20]; +}; + +typedef struct public_key_packet_t public_key_packet_t; +typedef struct signature_packet_v4_t signature_packet_v4_t; +typedef struct signature_packet_v3_t signature_packet_v3_t; + +struct public_key_t +{ + uint8_t longid[8]; /* Long id */ + uint8_t *psz_username; /* USER ID */ + + public_key_packet_t key; /* Public key packet */ + + signature_packet_v4_t sig; /* Signature packet, by the embedded key */ +}; + +typedef struct public_key_t public_key_t; + +/* We trust this public key, and by extension, also keys signed by it. */ + +//#define OLD 1 //Define OLD to use Videolan Key 2006, to test public key download + +static uint8_t videolan_public_key_longid[8] = { +#ifdef OLD + 0xC3, 0x67, 0xD8, 0xB9, 0x81, 0xCA, 0xCA, 0x84 +#else + 0x90, 0x28, 0x17, 0xE4, 0xAA, 0x5F, 0x4D, 0xE6 +#endif +}; + +static uint8_t videolan_public_key[] = { +#ifdef OLD +"-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +"Version: GnuPG v2.0.4 (FreeBSD)\n" +"\n" +"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\n" +"=tVe2\n" +"-----END PGP PUBLIC KEY BLOCK-----\n" +#else +"-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +"Version: GnuPG v2.0.4 (FreeBSD)\n" +"\n" +"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\n" +"=qrc1\n" +"-----END PGP PUBLIC KEY BLOCK-----\n" +#endif +}; + enum { UpdateReleaseStatusOlder, @@ -67,7 +277,6 @@ struct update_t struct update_release_t release; ///< Release (version) }; - #define update_New( a ) __update_New( VLC_OBJECT( a ) ) VLC_EXPORT( update_t *, __update_New, ( vlc_object_t * ) ); @@ -80,3 +289,5 @@ VLC_EXPORT( int, update_CompareReleaseToCurrent, ( update_t * ) ); */ #endif + +#endif diff --git a/modules/control/rc.c b/modules/control/rc.c index 3f9133dd48..94c4858165 100644 --- a/modules/control/rc.c +++ b/modules/control/rc.c @@ -96,7 +96,9 @@ static int AudioConfig ( vlc_object_t *, char const *, vlc_value_t, vlc_value_t, void * ); static int Menu ( vlc_object_t *, char const *, vlc_value_t, vlc_value_t, void * ); +#ifdef UPDATE_CHECK static void checkUpdates( intf_thread_t *p_intf ); +#endif /* Status Callbacks */ static int TimeOffsetChanged( vlc_object_t *, char const *, @@ -750,10 +752,12 @@ static void Run( intf_thread_t *p_intf ) Help( p_intf, b_longhelp ); } +#ifdef UPDATE_CHECK else if( !strcmp( psz_cmd, "check-updates" ) ) { checkUpdates( p_intf ); } +#endif else if( !strcmp( psz_cmd, "key" ) || !strcmp( psz_cmd, "hotkey" ) ) { var_SetInteger( p_intf->p_libvlc, "key-pressed", @@ -922,9 +926,11 @@ static void Help( intf_thread_t *p_intf, vlc_bool_t b_longhelp) msg_rc(_("| @name mosaic-cols #. . . . . . . . . . .number of cols")); msg_rc(_("| @name mosaic-order id(,id)* . . . . order of pictures ")); msg_rc(_("| @name mosaic-keep-aspect-ratio {0,1} . . .aspect ratio")); +#ifdef UPDATE_CHECK msg_rc( "| "); msg_rc(_("| check-updates [newer] [equal] [older]\n" "| [undef] [info] [source] [binary] [plugin]")); +#endif msg_rc( "| "); } msg_rc(_("| help . . . . . . . . . . . . . . . this help message")); @@ -1634,7 +1640,7 @@ static int VideoConfig( vlc_object_t *p_this, char const *psz_cmd, intf_thread_t *p_intf = (intf_thread_t*)p_this; input_thread_t *p_input = NULL; vout_thread_t * p_vout; - const char * psz_variable; + const char * psz_variable = NULL; int i_error; p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_ANYWHERE ); @@ -2105,6 +2111,7 @@ static input_item_t *parse_MRL( intf_thread_t *p_intf, char *psz_mrl ) /***************************************************************************** * checkUpdates : check for updates ****************************************************************************/ +#ifdef UPDATE_CHECK static void checkUpdates( intf_thread_t *p_intf ) { update_t *p_u = update_New( p_intf ); @@ -2122,3 +2129,4 @@ static void checkUpdates( intf_thread_t *p_intf ) msg_rc( "\n+----Last version" ); update_Delete( p_u ); } +#endif diff --git a/modules/gui/macosx/intf.h b/modules/gui/macosx/intf.h index b28b4f24ef..7e75880cda 100644 --- a/modules/gui/macosx/intf.h +++ b/modules/gui/macosx/intf.h @@ -101,7 +101,9 @@ struct intf_sys_t id o_embedded_list; /* VLCEmbeddedList*/ id o_interaction_list; /* VLCInteractionList*/ id o_sfilters; /* VLCsFilters */ +#ifdef UPDATE_CHECK id o_update; /* VLCUpdate */ +#endif id o_eyetv; /* VLCEyeTVController */ BOOL nib_main_loaded; /* main nibfile */ BOOL nib_open_loaded; /* open nibfile */ diff --git a/modules/gui/macosx/intf.m b/modules/gui/macosx/intf.m index 2d11e153f6..1e17a48b36 100644 --- a/modules/gui/macosx/intf.m +++ b/modules/gui/macosx/intf.m @@ -393,7 +393,9 @@ static VLCMain *_o_sharedMainInstance = nil; o_embedded_list = [[VLCEmbeddedList alloc] init]; o_interaction_list = [[VLCInteractionList alloc] init]; o_sfilters = nil; +#ifdef UPDATE_CHECK //FIXME o_update = [[VLCUpdate alloc] init]; +#endif i_lastShownVolume = -1; @@ -785,6 +787,7 @@ static VLCMain *_o_sharedMainInstance = nil; - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { +#ifdef UPDATE_CHECK /* Check for update silently on startup */ if ( !nib_update_loaded ) nib_update_loaded = [NSBundle loadNibNamed:@"Update" owner:self]; @@ -792,7 +795,8 @@ static VLCMain *_o_sharedMainInstance = nil; // FIXME //if([o_update shouldCheckForUpdate]) // [NSThread detachNewThreadSelector:@selector(checkForUpdate) toTarget:o_update withObject:NULL]; - +#endif + /* Handle sleep notification */ [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(computerWillSleep:) name:NSWorkspaceWillSleepNotification object:nil]; @@ -1909,6 +1913,7 @@ static VLCMain *_o_sharedMainInstance = nil; [o_prefs showPrefs]; } +#ifdef UPDATE_CHECK - (IBAction)checkForUpdate:(id)sender {/* FIXME if( !nib_update_loaded ) @@ -1916,6 +1921,7 @@ static VLCMain *_o_sharedMainInstance = nil; [o_update showUpdateWindow]; */} +#endif - (IBAction)viewHelp:(id)sender { diff --git a/modules/gui/macosx/update.h b/modules/gui/macosx/update.h index a0b2585836..52dc641991 100644 --- a/modules/gui/macosx/update.h +++ b/modules/gui/macosx/update.h @@ -21,6 +21,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. *****************************************************************************/ +#ifdef UPDATE_CHECK #import #import @@ -55,3 +56,4 @@ + (VLCUpdate *)sharedInstance; @end +#endif diff --git a/modules/gui/macosx/update.m b/modules/gui/macosx/update.m index 04fc5743a2..bc8052f484 100644 --- a/modules/gui/macosx/update.m +++ b/modules/gui/macosx/update.m @@ -27,6 +27,7 @@ * ../wxwidgets/dialogs/updatevlc.cpp, written by Antoine Cellerier. *****************************************************************************/ +#ifdef UPDATE_CHECK /***************************************************************************** * Preamble @@ -351,3 +352,5 @@ static VLCUpdate *_o_sharedInstance = nil; } @end + +#endif diff --git a/modules/gui/qt4/dialogs/help.cpp b/modules/gui/qt4/dialogs/help.cpp index 409dbafce3..44886269b7 100644 --- a/modules/gui/qt4/dialogs/help.cpp +++ b/modules/gui/qt4/dialogs/help.cpp @@ -22,9 +22,14 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ +#include + #include "dialogs/help.hpp" #include + +#ifdef UPDATE_CHECK #include +#endif #include "dialogs_provider.hpp" @@ -159,7 +164,7 @@ void AboutDialog::close() this->toggleVisible(); } - +#ifdef UPDATE_CHECK UpdateDialog *UpdateDialog::instance = NULL; UpdateDialog::UpdateDialog( intf_thread_t *_p_intf ) : QVLCFrame( _p_intf ) @@ -217,3 +222,5 @@ void UpdateDialog::updateOrUpload()
You have the latest version of VLC.
" ); } } + +#endif diff --git a/modules/gui/qt4/dialogs/help.hpp b/modules/gui/qt4/dialogs/help.hpp index 9a8df3cafa..a8fdb1d8bd 100644 --- a/modules/gui/qt4/dialogs/help.hpp +++ b/modules/gui/qt4/dialogs/help.hpp @@ -24,7 +24,7 @@ #ifndef _HELP_DIALOG_H_ #define _HELP_DIALOG_H_ -#include +#include #include "util/qvlcframe.hpp" @@ -70,7 +70,7 @@ public slots: void close(); }; - +#ifdef UPDATE_CHECK class UpdateDialog : public QVLCFrame { Q_OBJECT; @@ -92,6 +92,6 @@ private slots: void close(); void updateOrUpload(); }; - +#endif #endif diff --git a/modules/gui/qt4/dialogs_provider.cpp b/modules/gui/qt4/dialogs_provider.cpp index 9e02240bf3..7d425611f6 100644 --- a/modules/gui/qt4/dialogs_provider.cpp +++ b/modules/gui/qt4/dialogs_provider.cpp @@ -172,10 +172,12 @@ void DialogsProvider::helpDialog() HelpDialog::getInstance( p_intf )->toggleVisible(); } +#ifdef UPDATE_CHECK void DialogsProvider::updateDialog() { UpdateDialog::getInstance( p_intf )->toggleVisible(); } +#endif void DialogsProvider::aboutDialog() { diff --git a/modules/gui/qt4/dialogs_provider.hpp b/modules/gui/qt4/dialogs_provider.hpp index f7d6989fbb..2d3533bd14 100644 --- a/modules/gui/qt4/dialogs_provider.hpp +++ b/modules/gui/qt4/dialogs_provider.hpp @@ -146,7 +146,9 @@ public slots: void messagesDialog(); void vlmDialog(); void helpDialog(); +#ifdef UPDATE_CHECK void updateDialog(); +#endif void aboutDialog(); void gotoTimeDialog(); void podcastConfigureDialog(); diff --git a/modules/gui/qt4/menus.cpp b/modules/gui/qt4/menus.cpp index b694ab1558..0a171b5c30 100644 --- a/modules/gui/qt4/menus.cpp +++ b/modules/gui/qt4/menus.cpp @@ -22,6 +22,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ +#include + #include #include "main_interface.hpp" @@ -457,7 +459,9 @@ QMenu *QVLCMenu::HelpMenu() QMenu *menu = new QMenu(); addDPStaticEntry( menu, qtr( "Help..." ) , "", ":/pixmaps/menus_help_16px.png", SLOT( helpDialog() ), "F1" ); +#ifdef UPDATE_CHECK addDPStaticEntry( menu, qtr( "Update" ) , "", "", SLOT( updateDialog() ), ""); +#endif menu->addSeparator(); addDPStaticEntry( menu, qtr( I_MENU_ABOUT ), "", "", SLOT( aboutDialog() ), "Ctrl+F1" ); diff --git a/modules/gui/wxwidgets/dialogs.cpp b/modules/gui/wxwidgets/dialogs.cpp index 12e964753a..63f062a7d9 100644 --- a/modules/gui/wxwidgets/dialogs.cpp +++ b/modules/gui/wxwidgets/dialogs.cpp @@ -63,7 +63,9 @@ private: void Open( int i_access_method, int i_arg ); /* Event handlers (these functions should _not_ be virtual) */ +#ifdef UPDATE_CHECK void OnUpdateVLC( wxCommandEvent& event ); +#endif //void OnVLM( wxCommandEvent& event ); void OnInteraction( wxCommandEvent& event ); void OnExit( wxCommandEvent& event ); @@ -108,7 +110,9 @@ public: wxFrame *p_prefs_dialog; wxFrame *p_bookmarks_dialog; wxFileDialog *p_file_generic_dialog; +#ifdef UPDATE_CHECK UpdateVLC *p_updatevlc_dialog; +#endif //VLMFrame *p_vlm_dialog; }; } @@ -156,8 +160,10 @@ BEGIN_EVENT_TABLE(DialogsProvider, wxFrame) EVT_COMMAND(INTF_DIALOG_EXIT, wxEVT_DIALOG, DialogsProvider::OnExitThread) +#ifdef UPDATE_CHECK EVT_COMMAND(INTF_DIALOG_UPDATEVLC, wxEVT_DIALOG, DialogsProvider::OnUpdateVLC) +#endif #if 0 EVT_COMMAND(INTF_DIALOG_VLM, wxEVT_DIALOG, DialogsProvider::OnVLM) @@ -549,6 +555,7 @@ void DialogsProvider::OnExitThread( wxCommandEvent& WXUNUSED(event) ) wxTheApp->ExitMainLoop(); } +#ifdef UPDATE_CHECK void DialogsProvider::OnUpdateVLC( wxCommandEvent& WXUNUSED(event) ) { /* Show/hide the file info window */ @@ -560,6 +567,7 @@ void DialogsProvider::OnUpdateVLC( wxCommandEvent& WXUNUSED(event) ) p_updatevlc_dialog->Show( !p_updatevlc_dialog->IsShown() ); } } +#endif #if 0 void DialogsProvider::OnVLM( wxCommandEvent& WXUNUSED(event) ) diff --git a/modules/gui/wxwidgets/dialogs/updatevlc.cpp b/modules/gui/wxwidgets/dialogs/updatevlc.cpp index b4e0f6d6f6..88eb5e0eaa 100644 --- a/modules/gui/wxwidgets/dialogs/updatevlc.cpp +++ b/modules/gui/wxwidgets/dialogs/updatevlc.cpp @@ -21,6 +21,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ +#ifdef UPDATE_CHECK + /***************************************************************************** * Preamble *****************************************************************************/ @@ -120,3 +122,4 @@ void UpdateVLC::OnCheckForUpdate( wxCommandEvent& event ) SetSizerAndFit( main_sizer ); Layout(); } +#endif diff --git a/modules/gui/wxwidgets/dialogs/updatevlc.hpp b/modules/gui/wxwidgets/dialogs/updatevlc.hpp index 29cf974a36..53cba16633 100644 --- a/modules/gui/wxwidgets/dialogs/updatevlc.hpp +++ b/modules/gui/wxwidgets/dialogs/updatevlc.hpp @@ -21,6 +21,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ +#ifdef UPDATE_CHECK + #ifndef _WXVLC_UPDATEVLC_H_ #define _WXVLC_UPDATEVLC_H_ @@ -54,3 +56,5 @@ namespace wxvlc }; #endif + +#endif diff --git a/modules/gui/wxwidgets/interface.cpp b/modules/gui/wxwidgets/interface.cpp index 58ed75c222..fa73e2776d 100644 --- a/modules/gui/wxwidgets/interface.cpp +++ b/modules/gui/wxwidgets/interface.cpp @@ -300,7 +300,9 @@ enum About_Event = wxID_ABOUT, OnWebLink_Event, OnWebHelp_Event, +#ifdef UPDATE_CHECK UpdateVLC_Event, +#endif //VLM_Event, Iconize_Event, @@ -314,7 +316,9 @@ BEGIN_EVENT_TABLE(Interface, wxFrame) EVT_MENU(About_Event, Interface::OnAbout) EVT_MENU(OnWebLink_Event, Interface::OnWebLink) EVT_MENU(OnWebHelp_Event, Interface::OnWebHelp) +#ifdef UPDATE_CHECK EVT_MENU(UpdateVLC_Event, Interface::OnShowDialog) +#endif //EVT_MENU(VLM_Event, Interface::OnShowDialog) EVT_MENU(Playlist_Event, Interface::OnShowDialog) @@ -617,8 +621,10 @@ void Interface::CreateOurMenuBar() help_menu->Append( OnWebHelp_Event, wxU(_("Online Help")) ); help_menu->AppendSeparator(); help_menu->Append( About_Event, wxU(_("About...")) ); +#ifdef UPDATE_CHECK help_menu->AppendSeparator(); help_menu->Append( UpdateVLC_Event, wxU(_("Check for Updates...")) ); +#endif /* Append the freshly created menus to the menu bar... */ wxMenuBar *menubar = new wxMenuBar(); @@ -1024,9 +1030,11 @@ void Interface::OnShowDialog( wxCommandEvent& event ) case Bookmarks_Event: i_id = INTF_DIALOG_BOOKMARKS; break; +#ifdef UPDATE_CHECK case UpdateVLC_Event: i_id = INTF_DIALOG_UPDATEVLC; break; +#endif #if 0 case VLM_Event: i_id = INTF_DIALOG_VLM; diff --git a/src/misc/update.c b/src/misc/update.c index 2ba4773bc5..14a930b311 100644 --- a/src/misc/update.c +++ b/src/misc/update.c @@ -26,12 +26,19 @@ * This file contains functions related to VLC and plugins update management */ +/* + * TODO: * pgp verification of the update file + * binary download, and pgp verification + */ + /***************************************************************************** * Preamble *****************************************************************************/ #include +#ifdef UPDATE_CHECK + #include /* tolower() */ #include @@ -76,6 +83,658 @@ static int extracmp( char *psz_1, char *psz_2 ); static int CompareReleases( const struct update_release_t *p1, const struct update_release_t *p2 ); +/***************************************************************************** + * OpenPGP functions + *****************************************************************************/ + +#define packet_type( c ) ( ( c & 0x3c ) >> 2 ) /* 0x3C = 00111100 */ +#define packet_header_len( c ) ( ( c & 0x03 ) + 1 ) /* number of bytes in a packet header */ + +static inline int scalar_number( uint8_t *p, int header_len ) +{ + if( header_len == 1 ) + return( p[0] ); + else if( header_len == 2 ) + return( (p[0] << 8) + p[1] ); + else if( header_len == 4 ) + return( (p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3] ); + else + abort(); +} + +/* number of data bytes in a MPI */ +#define mpi_len( mpi ) ( ( scalar_number( mpi, 2 ) + 7 ) / 8 ) + +/* + * fill a public_key_packet_t structure from public key packet data + * verify that it is a version 4 public key packet, using DSA + */ +static int parse_public_key_packet( public_key_packet_t *p_key, uint8_t *p_buf, + size_t i_packet_len ) +{ + if( i_packet_len != 418 ) + return VLC_EGENERIC; + + p_key->version = *p_buf++; + if( p_key->version != 4 ) + return VLC_EGENERIC; + + /* warn when timestamp is > date ? */ + memcpy( p_key->timestamp, p_buf, 4 ); p_buf += 4; + + p_key->algo = *p_buf++; + if( p_key->algo != PUBLIC_KEY_ALGO_DSA ) + return VLC_EGENERIC; + + memcpy( p_key->p, p_buf, 2+128 ); p_buf += 2+128; + if( mpi_len( p_key->p ) != 128 ) + return VLC_EGENERIC; + + memcpy( p_key->q, p_buf, 2+20 ); p_buf += 2+20; + if( mpi_len( p_key->q ) != 20 ) + return VLC_EGENERIC; + + memcpy( p_key->g, p_buf, 2+128 ); p_buf += 2+128; + if( mpi_len( p_key->g ) != 128 ) + return VLC_EGENERIC; + + memcpy( p_key->y, p_buf, 2+128 ); p_buf += 2+128; + if( mpi_len( p_key->y ) != 128 ) + return VLC_EGENERIC; + + return VLC_SUCCESS; +} + +/* + * fill a signature_packet_v4_t from signature packet data + * verify that it was used with a DSA public key, using SHA-1 digest + */ +static int parse_signature_v4_packet( signature_packet_v4_t *p_sig, + uint8_t *p_buf, size_t i_sig_len ) +{ + if( i_sig_len < 54 ) + return VLC_EGENERIC; + + p_sig->version = *p_buf++; + if( p_sig->version != 4 ) + return VLC_EGENERIC; + + p_sig->type = *p_buf++; + if( p_sig->type < GENERIC_KEY_SIGNATURE || + p_sig->type > POSITIVE_KEY_SIGNATURE ) + return VLC_EGENERIC; + + p_sig->public_key_algo = *p_buf++; + if( p_sig->public_key_algo != PUBLIC_KEY_ALGO_DSA ) + return VLC_EGENERIC; + + p_sig->digest_algo = *p_buf++; + if( p_sig->digest_algo != DIGEST_ALGO_SHA1 ) + return VLC_EGENERIC; + + memcpy( p_sig->hashed_data_len, p_buf, 2 ); p_buf += 2; + + size_t i_pos = 6; + size_t i_hashed_data_len = scalar_number( p_sig->hashed_data_len, 2 ); + i_pos += i_hashed_data_len; + if( i_pos > i_sig_len - 48 ) /* r & s are 44 bytes in total, + * + the unhashed data length (2 bytes) + * + the hash verification (2 bytes) */ + return VLC_EGENERIC; + + p_sig->hashed_data = (uint8_t*) malloc( i_hashed_data_len ); + if( !p_sig->hashed_data ) + return VLC_ENOMEM; + memcpy( p_sig->hashed_data, p_buf, i_hashed_data_len ); + p_buf += i_hashed_data_len; + + memcpy( p_sig->unhashed_data_len, p_buf, 2 ); p_buf += 2; + + size_t i_unhashed_data_len = scalar_number( p_sig->unhashed_data_len, 2 ); + i_pos += 2 + i_unhashed_data_len; + if( i_pos != i_sig_len - 46 ) + { + free( p_sig->hashed_data ); + return VLC_EGENERIC; + } + + p_sig->unhashed_data = (uint8_t*) malloc( i_unhashed_data_len ); + if( !p_sig->unhashed_data ) + { + free( p_sig->hashed_data ); + return VLC_ENOMEM; + } + memcpy( p_sig->unhashed_data, p_buf, i_unhashed_data_len ); + p_buf += i_unhashed_data_len; + + memcpy( p_sig->hash_verification, p_buf, 2 ); p_buf += 2; + + memcpy( p_sig->r, p_buf, 22 ); p_buf += 22; + if( mpi_len( p_sig->r ) != 20 ) + { + free( p_sig->hashed_data ); + free( p_sig->unhashed_data ); + return VLC_EGENERIC; + } + + memcpy( p_sig->s, p_buf, 22 ); + if( mpi_len( p_sig->s ) != 20 ) + { + free( p_sig->hashed_data ); + free( p_sig->unhashed_data ); + return VLC_EGENERIC; + } + + return VLC_SUCCESS; +} + +/* + * crc_octets() was lamely copied from rfc 2440 + * Copyright (C) The Internet Society (1998). All Rights Reserved. + */ +#define CRC24_INIT 0xB704CEL +#define CRC24_POLY 0x1864CFBL + +static long crc_octets( uint8_t *octets, size_t len ) +{ + long crc = CRC24_INIT; + int i; + while (len--) + { + crc ^= (*octets++) << 16; + for (i = 0; i < 8; i++) + { + crc <<= 1; + if (crc & 0x1000000) + crc ^= CRC24_POLY; + } + } + return crc & 0xFFFFFFL; +} + +/* + * Transform an armored document in binary format + * Used on public keys and signatures + */ +static int pgp_unarmor( char *p_ibuf, size_t i_ibuf_len, + uint8_t *p_obuf, size_t i_obuf_len ) +{ + char *p_ipos = p_ibuf; + uint8_t *p_opos = p_obuf; + int i_end = 0; + + int i_header_skipped = 0; + + while( !i_end && p_ipos < p_ibuf + i_ibuf_len ) + { + if( *p_ipos == '\r' || *p_ipos == '\n' ) + { + p_ipos++; + continue; + } + + size_t i_line_len = strcspn( p_ipos, "\r\n" ); + if( i_line_len == 0 ) + continue; + + if( !i_header_skipped ) + { + if( !strncmp( p_ipos, "-----BEGIN PGP", 14 ) ) + i_header_skipped = 1; + + p_ipos += i_line_len + 1; + continue; + } + + if( !strncmp( p_ipos, "Version:", 8 ) ) + { + p_ipos += i_line_len + 1; + continue; + } + + if( p_ipos[i_line_len - 1] == '=' ) + { + i_end = 1; + p_ipos[i_line_len - 1] = '\0'; + } + else + p_ipos[i_line_len] = '\0'; + + p_opos += vlc_b64_decode_binary_to_buffer( p_opos, + p_obuf - p_opos + i_obuf_len, + p_ipos ); + + p_ipos += i_line_len + 1; + } + + /* XXX: the CRC is OPTIONAL, really require it ? */ + if( p_ipos + 5 > p_ibuf + i_ibuf_len || *p_ipos++ != '=' ) + return 0; + + uint8_t p_crc[3]; + if( vlc_b64_decode_binary_to_buffer( p_crc, 3, p_ipos ) != 3 ) + return 0; + + long l_crc = crc_octets( p_obuf, p_opos - p_obuf ); + long l_crc2 = ( 0 << 24 ) + ( p_crc[0] << 16 ) + ( p_crc[1] << 8 ) + p_crc[2]; + + return l_crc2 == l_crc ? p_opos - p_obuf : 0; +} + +/* + * Download the signature associated to a document or a binary file. + * We're given the file's url, we just append ".asc" to it and download + */ +static int download_signature( vlc_object_t *p_this, + signature_packet_v3_t *p_sig, char *psz_url ) +{ + char *psz_sig = (char*) malloc( strlen( psz_url ) + 4 + 1 ); /* ".asc" + \0 */ + if( !psz_sig ) + return VLC_ENOMEM; + + strcpy( psz_sig, psz_url ); + strcat( psz_sig, ".asc" ); + + stream_t *p_stream = stream_UrlNew( p_this, psz_sig ); + free( psz_sig ); + + if( !p_stream ) + return VLC_ENOMEM; + + int64_t i_size = stream_Size( p_stream ); + if( i_size < 65 ) + { + stream_Delete( p_stream ); + return VLC_EGENERIC; + } + else if( i_size == 65 ) /* binary format signature */ + { + int i_read = stream_Read( p_stream, p_sig, (int)i_size ); + stream_Delete( p_stream ); + if( i_read != i_size ) + return VLC_EGENERIC; + else + return VLC_SUCCESS; + } + + char *p_buf = (char*)malloc( i_size ); + if( !p_buf ) + { + stream_Delete( p_stream ); + return VLC_ENOMEM; + } + + int i_read = stream_Read( p_stream, p_buf, (int)i_size ); + + stream_Delete( p_stream ); + + if( i_read != i_size ) + { + free( p_buf ); + return VLC_EGENERIC; + } + + int i_bytes = pgp_unarmor( p_buf, i_size, (uint8_t*)p_sig, 65 ); + free( p_buf ); + + if( i_bytes != 65 ) + return VLC_EGENERIC; + else + return VLC_SUCCESS; +} + +/* + * Verify an OpenPGP signature made on some SHA-1 hash, with some DSA public key + */ +static int verify_signature( vlc_object_t *p_this, uint8_t *p_r, uint8_t *p_s, + public_key_packet_t *p_key, uint8_t *p_hash ) +{ + /* the data to be verified (a SHA-1 hash) */ + const char *hash_sexp_s = "(data(flags raw)(value %m))"; + /* the public key */ + const char *key_sexp_s = "(public-key(dsa(p %m)(q %m)(g %m)(y %m)))"; + /* the signature */ + const char *sig_sexp_s = "(sig-val(dsa(r %m )(s %m )))"; + + size_t erroff; + gcry_mpi_t p, q, g, y, r, s, hash; + p = q = g = y = r = s = hash = NULL; + gcry_sexp_t key_sexp, hash_sexp, sig_sexp; + key_sexp = hash_sexp = sig_sexp = NULL; + + if( gcry_mpi_scan( &p, GCRYMPI_FMT_USG, p_key->p + 2, 128, NULL ) || + gcry_mpi_scan( &q, GCRYMPI_FMT_USG, p_key->q + 2, 20, NULL ) || + gcry_mpi_scan( &g, GCRYMPI_FMT_USG, p_key->g + 2, 128, NULL ) || + gcry_mpi_scan( &y, GCRYMPI_FMT_USG, p_key->y + 2, 128, NULL ) || + gcry_sexp_build( &key_sexp, &erroff, key_sexp_s, p, q, g, y ) ) + goto problem; + + if( gcry_mpi_scan( &r, GCRYMPI_FMT_USG, p_r + 2, 20, NULL ) || + gcry_mpi_scan( &s, GCRYMPI_FMT_USG, p_s + 2, 20, NULL ) || + gcry_sexp_build( &sig_sexp, &erroff, sig_sexp_s, r, s ) ) + goto problem; + + if( gcry_mpi_scan( &hash, GCRYMPI_FMT_USG, p_hash, 20, NULL ) || + gcry_sexp_build( &hash_sexp, &erroff, hash_sexp_s, hash ) ) + goto problem; + + if( gcry_pk_verify( sig_sexp, hash_sexp, key_sexp ) ) + goto problem; + + return VLC_SUCCESS; + +problem: + if( p ) gcry_mpi_release( p ); + if( q ) gcry_mpi_release( q ); + if( g ) gcry_mpi_release( g ); + if( y ) gcry_mpi_release( y ); + if( r ) gcry_mpi_release( r ); + if( s ) gcry_mpi_release( s ); + if( hash ) gcry_mpi_release( hash ); + if( key_sexp ) gcry_sexp_release( key_sexp ); + if( sig_sexp ) gcry_sexp_release( sig_sexp ); + if( hash_sexp ) gcry_sexp_release( hash_sexp ); + return VLC_EGENERIC; +} + +/* + * Return the long id (8 bytes) of the public key used to generate a signature + */ +static uint8_t *get_issuer_from_signature_v4( signature_packet_v4_t *p_sig ) +{ + uint8_t *p = p_sig->unhashed_data; + uint8_t *max_pos = p + scalar_number( p_sig->unhashed_data_len, 2 ); + + while( p < max_pos ) + { + int i_subpacket_len = *p < 192 ? *p++ : + *p < 255 ? ((*p++ - 192) << 8) + *p++ + 192 : + ((*++p) << 24) + (*++p << 16) + (*++p << 8) + *++p; + + if( p >= max_pos - 1 ) + return NULL; + + if( *p == ISSUER_SUBPACKET ) + return p+1; + else + p += i_subpacket_len; + } + return NULL; +} + +/* + * fill a public_key_t with public key data, including: + * * public key packet + * * signature packet issued by key which long id is p_sig_issuer + * * user id packet + */ +static int parse_public_key( const uint8_t *p_key_data, size_t i_key_len, public_key_t *p_key, const uint8_t *p_sig_issuer ) +{ + uint8_t *pos = (uint8_t*) p_key_data; + uint8_t *max_pos = pos + i_key_len; + + int i_status = 0; +#define PUBLIC_KEY_FOUND 0x01 +#define USER_ID_FOUND 0x02 +#define SIGNATURE_FOUND 0X04 + + uint8_t *p_key_unarmored = NULL; + + signature_packet_v4_t sig; + + p_key->psz_username = NULL; + p_key->sig.hashed_data = p_key->sig.unhashed_data = NULL; + + if( !( *pos & 0x80 ) ) + { /* first byte is ASCII, unarmoring */ + p_key_unarmored = (uint8_t*)malloc( i_key_len ); + if( !p_key_unarmored ) + return VLC_ENOMEM; + int i_len = pgp_unarmor( (char*)p_key_data, i_key_len, + p_key_unarmored, i_key_len ); + + if( i_len == 0 ) + goto error; + + pos = p_key_unarmored; + max_pos = pos + i_len; + } + + while( pos < max_pos ) + { + if( !(*pos & 0x80) || *pos & 0x40 ) + goto error; + + int i_type = packet_type( *pos ); + + int i_header_len = packet_header_len( *pos++ ); + if( pos + i_header_len > max_pos ) + goto error; + + int i_packet_len = scalar_number( pos, i_header_len ); + pos += i_header_len; + + if( pos + i_packet_len > max_pos ) + goto error; + + switch( i_type ) + { + uint8_t *p_issuer; + + case PUBLIC_KEY_PACKET: + i_status |= PUBLIC_KEY_FOUND; + if( parse_public_key_packet( &p_key->key, pos, i_packet_len ) != VLC_SUCCESS ) + goto error; + break; + + case SIGNATURE_PACKET: + if( !p_sig_issuer || i_status & SIGNATURE_FOUND || + parse_signature_v4_packet( &sig, pos, i_packet_len ) != VLC_SUCCESS ) + break; + p_issuer = get_issuer_from_signature_v4( &sig ); + if( memcmp( p_issuer, p_sig_issuer, 8 ) == 0 ) + { + memcpy( &p_key->sig, &sig, sizeof( signature_packet_v4_t ) ); + i_status |= SIGNATURE_FOUND; + } + else + { + free( sig.hashed_data ); + free( sig.unhashed_data ); + } + break; + + case USER_ID_PACKET: + if( p_key->psz_username ) /* save only the first User ID */ + break; + i_status |= USER_ID_FOUND; + p_key->psz_username = (uint8_t*)malloc( i_packet_len + 1); + if( !p_key->psz_username ) + goto error; + + memcpy( p_key->psz_username, pos, i_packet_len ); + p_key->psz_username[i_packet_len] = '\0'; + break; + + default: + break; + } + pos += i_packet_len; + } + free( p_key_unarmored ); + + if( !( i_status & ( PUBLIC_KEY_FOUND + USER_ID_FOUND ) ) ) + return VLC_EGENERIC; + + if( p_sig_issuer && !( i_status & SIGNATURE_FOUND ) ) + return VLC_EGENERIC; + + return VLC_SUCCESS; + +error: + free( p_key->sig.hashed_data ); + free( p_key->sig.unhashed_data ); + free( p_key->psz_username ); + free( p_key_unarmored ); + return VLC_EGENERIC; +} + +/* + * return a sha1 hash of a file + */ +static uint8_t *hash_sha1_from_file( const char *psz_file, + signature_packet_v3_t *p_sig ) +{ + FILE *f = utf8_fopen( psz_file, "r" ); + if( !f ) + return NULL; + + uint8_t buffer[4096]; //FIXME + + gcry_md_hd_t hd; + if( gcry_md_open( &hd, GCRY_MD_SHA1, 0 ) ) + { + fclose( f ); + return NULL; + } + + size_t i_read; + while( ( i_read = fread( buffer, 1, sizeof(buffer), f ) ) > 0 ) + gcry_md_write( hd, buffer, i_read ); + + gcry_md_putc( hd, p_sig->type ); + gcry_md_write( hd, &p_sig->timestamp, 4 ); + + fclose( f ); + gcry_md_final( hd ); + + return( (uint8_t*) gcry_md_read( hd, GCRY_MD_SHA1) ); +} + +/* + * download a public key (the last one) from videolan server, and parse it + */ +static public_key_t *download_key( vlc_object_t *p_this, const uint8_t *p_longid, const uint8_t *p_signature_issuer ) +{ + char *psz_url; + if( asprintf( &psz_url, "http://download.videolan.org/pub/keys/%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x.asc", + p_longid[0], p_longid[1], + p_longid[2], p_longid[3], + p_longid[4], p_longid[5], + p_longid[6], p_longid[7] ) == -1 ) + return NULL; + + stream_t *p_stream = stream_UrlNew( p_this, psz_url ); + free( psz_url ); + if( !p_stream ) + return NULL; + + int64_t i_size = stream_Size( p_stream ); + if( i_size < 0 ) + { + stream_Delete( p_stream ); + return NULL; + } + + uint8_t *p_buf = (uint8_t*)malloc( i_size ); + if( !p_buf ) + { + stream_Delete( p_stream ); + return NULL; + } + + int i_read = stream_Read( p_stream, p_buf, (int)i_size ); + stream_Delete( p_stream ); + + if( i_read != (int)i_size ) + { + free( p_buf ); + return NULL; + } + + public_key_t *p_pkey = (public_key_t*) malloc( sizeof( public_key_t ) ); + if( !p_pkey ) + { + free( p_buf ); + return NULL; + } + + int i_error = parse_public_key( p_buf, i_read, p_pkey, p_signature_issuer ); + free( p_buf ); + + if( i_error != VLC_SUCCESS ) + { + free( p_pkey ); + return NULL; + } + + return p_pkey; +} + +/* + * Generate a SHA-1 hash on a public key, to verify a signature made on that hash + * Note that we need the signature to compute the hash + */ +static uint8_t *key_sign_hash( public_key_t *p_pkey ) +{ + gcry_error_t error = 0; + gcry_md_hd_t hd; + + error = gcry_md_open( &hd, GCRY_MD_SHA1, 0 ); + if( error ) + return NULL; + + gcry_md_putc( hd, 0x99 ); + + gcry_md_putc( hd, (418 >> 8) & 0xff ); + gcry_md_putc( hd, 418 & 0xff ); + + gcry_md_write( hd, (uint8_t*)&p_pkey->key, 418 ); + + gcry_md_putc( hd, 0xb4 ); + + int i_len = strlen((char*)p_pkey->psz_username); + + gcry_md_putc( hd, (i_len << 24) & 0xff ); + gcry_md_putc( hd, (i_len << 16) & 0xff ); + gcry_md_putc( hd, (i_len << 8) & 0xff ); + gcry_md_putc( hd, (i_len) & 0xff ); + + gcry_md_write( hd, p_pkey->psz_username, i_len ); + + size_t i_hashed_data_len = scalar_number( p_pkey->sig.hashed_data_len, 2 ); + + gcry_md_putc( hd, p_pkey->sig.version ); + gcry_md_putc( hd, p_pkey->sig.type ); + gcry_md_putc( hd, p_pkey->sig.public_key_algo ); + gcry_md_putc( hd, p_pkey->sig.digest_algo ); + gcry_md_write( hd, p_pkey->sig.hashed_data_len, 2 ); + gcry_md_write( hd, p_pkey->sig.hashed_data, i_hashed_data_len ); + + gcry_md_putc( hd, 0x04 ); + gcry_md_putc( hd, 0xff ); + + i_hashed_data_len += 6; /* hashed data + 6 bytes header */ + + gcry_md_putc( hd, (i_hashed_data_len << 24) & 0xff); + gcry_md_putc( hd, (i_hashed_data_len << 16) &0xff ); + gcry_md_putc( hd, (i_hashed_data_len << 8) & 0xff ); + gcry_md_putc( hd, (i_hashed_data_len) & 0xff ); + + gcry_md_final( hd ); + + uint8_t *p_hash = gcry_md_read( hd, GCRY_MD_SHA1); + + if( p_hash[0] != p_pkey->sig.hash_verification[0] || + p_hash[1] != p_pkey->sig.hash_verification[1] ) + { + free( p_hash ); + return NULL; + } + + return p_hash; +} + /***************************************************************************** * Update_t functions @@ -325,3 +984,5 @@ int update_CompareReleaseToCurrent( update_t *p_update ) } return i_result; } + +#endif -- 2.39.2