Add "File -> Export -> SSL Session Keys..." to be able to save the keyring info for each session in the trace file. This makes it possible to give someone the trace and the exported keys so that they can decrypt the traffic in the trace, but not new sessions to the same server.

(See also: https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=3444)


svn path=/trunk/; revision=37446
This commit is contained in:
Sake Blok 2011-05-28 18:17:50 +00:00
parent 31c756a6d1
commit ba086053c7
12 changed files with 475 additions and 2 deletions

View File

@ -2795,6 +2795,7 @@ Sake Blok <sake [AT] euronet.nl> {
Ignore all packets functionality
Enable printing of all occurrences of fields
Follow TCP stream enhancements
Export SSL Session Keys
Miscellaneous enhancements and fixes
}

View File

@ -3349,7 +3349,7 @@ ssl_keylog_lookup(SslDecryptSession* ssl_session,
offset = 4;
if ( memcmp(line+4,"Session-ID:",11) == 0 ) {
if ( ssl_session->session_id.data_len>0 && memcmp(line+offset,"Session-ID:",11) == 0 ) {
offset += 11;
for (i = 0; i < ssl_session->session_id.data_len; i++) {
if (from_hex_char(line[offset + i*2]) != (ssl_session->session_id.data[i] >> 4) ||

View File

@ -289,7 +289,9 @@ static const fragment_items ssl_segment_items = {
"Segments"
};
static GHashTable *ssl_session_hash = NULL;
/* ssl_session_hash is used by "Export SSL Session Keys" */
GHashTable *ssl_session_hash = NULL;
static GHashTable *ssl_key_hash = NULL;
static GTree* ssl_associations = NULL;
static dissector_handle_t ssl_handle = NULL;

View File

@ -25,6 +25,8 @@
#ifndef __PACKET_SSL_H__
#define __PACKET_SSL_H__
WS_VAR_IMPORT GHashTable *ssl_session_hash;
extern void ssl_dissector_add(guint port, const gchar *protocol, gboolean tcp);
extern void ssl_dissector_delete(guint port, const gchar *protocol, gboolean tcp);

View File

@ -974,6 +974,7 @@ srtp_add_address
ssl_dissector_add
ssl_dissector_delete
ssl_set_master_secret
ssl_session_hash DATA
start_requested_stats
stats_tree_branch_max_namelen
stats_tree_branch_to_str

View File

@ -68,6 +68,7 @@ WIRESHARK_GTK_SRC = \
export_object_dicom.c \
export_object_http.c \
export_object_smb.c \
export_sslkeys.c \
filter_autocomplete.c \
file_dlg.c \
file_import_dlg.c \
@ -249,6 +250,7 @@ noinst_HEADERS = \
expert_comp_table.h \
expert_indicators.h \
export_object.h \
export_sslkeys.h \
file_dlg.h \
file_import_dlg.h \
fileset_dlg.h \

266
gtk/export_sslkeys.c Normal file
View File

@ -0,0 +1,266 @@
/* export_sslkeys.c
*
* $Id$
*
* Export SSL Session Keys dialog
* by Sake Blok <sake@euronet.nl> (20110526)
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* 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
* of the License, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <ctype.h>
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <wsutil/file_util.h>
#include <string.h>
#include <epan/filesystem.h>
#include <epan/packet.h>
#include <epan/epan_dissect.h>
#include <epan/charsets.h>
#include <epan/prefs.h>
#include <epan/dissectors/packet-ssl.h>
#include <epan/dissectors/packet-ssl-utils.h>
#include "../simple_dialog.h"
#include "../isprint.h"
#include "../alert_box.h"
#include "../progress_dlg.h"
#include "../ui_util.h"
#include "gtk/keys.h"
#include "gtk/color_utils.h"
#include "gtk/capture_file_dlg.h"
#include "gtk/packet_win.h"
#include "gtk/file_dlg.h"
#include "gtk/gui_utils.h"
#include "gtk/gtkglobals.h"
#include "gtk/font_utils.h"
#include "gtk/webbrowser.h"
#include "gtk/main.h"
#include "gtk/menus.h"
#include "gtk/recent.h"
#include "gtk/export_sslkeys.h"
#ifdef _WIN32
#include <gdk/gdkwin32.h>
#include <windows.h>
#include "win32/file_dlg_win32.h"
#endif
void
ssl_export_sessions_func(gpointer key, gpointer value, gpointer user_data)
{
guint i,offset;
StringInfo* sslid = (StringInfo*)key;
StringInfo* mastersecret = (StringInfo*)value;
StringInfo* keylist = (StringInfo*)user_data;
offset = strlen(keylist->data);
g_snprintf(keylist->data+offset,keylist->data_len-offset,"RSA Session-ID:");
offset += 15;
for( i=0; i<sslid->data_len; i++) {
g_snprintf(keylist->data+offset,keylist->data_len-offset,"%.2x",sslid->data[i]&255);
offset += 2;
}
g_snprintf(keylist->data+offset,keylist->data_len-offset," Master-Key:");
offset += 12;
for( i=0; i<mastersecret->data_len; i++) {
g_snprintf(keylist->data+offset,keylist->data_len-offset,"%.2x",mastersecret->data[i]&255);
offset += 2;
}
g_snprintf(keylist->data+offset,keylist->data_len-offset,"\n");
}
StringInfo*
ssl_export_sessions(GHashTable *session_hash)
{
StringInfo* keylist;
/* Output format is:
* "RSA Session-ID:xxxx Master-Key:yyyy\n"
* Where xxxx is the session ID in hex (max 64 chars)
* Where yyyy is the Master Key in hex (always 96 chars)
* So in total max 3+1+11+64+1+11+96+2 = 189 chars
*/
keylist = g_malloc0(sizeof(StringInfo)+189*g_hash_table_size (session_hash));
keylist->data = ((guchar*)keylist+sizeof(StringInfo));
keylist->data_len = sizeof(StringInfo)+189*g_hash_table_size (session_hash);
g_hash_table_foreach(session_hash, ssl_export_sessions_func, (gpointer)keylist);
return keylist;
}
static GtkWidget *savesslkeys_dlg=NULL;
static void
savesslkeys_dlg_destroy_cb(GtkWidget *w _U_, gpointer user_data _U_)
{
savesslkeys_dlg = NULL;
}
/* save the SSL Session Keys */
static gboolean
savesslkeys_save_clicked_cb(GtkWidget * w _U_, gpointer data _U_)
{
int fd;
char *file;
StringInfo *keylist;
file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(savesslkeys_dlg));
if (test_for_directory(file) == EISDIR) {
/* It's a directory - set the file selection box to display that
directory, and leave the selection box displayed. */
set_last_open_dir(file);
g_free(file);
file_selection_set_current_folder(savesslkeys_dlg, get_last_open_dir());
gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(savesslkeys_dlg), "");
return FALSE; /* do gtk_dialog_run again */
}
/* XXX: Must check if file name exists first */
/*
* Retrieve the info we need
*/
keylist = ssl_export_sessions(ssl_session_hash);
if (keylist->data_len == 0 ) {
simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
"No SSL Session Keys to export!");
g_free(keylist);
g_free(file);
return TRUE;
}
fd = ws_open(file, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666);
if (fd == -1) {
open_failure_alert_box(file, errno, TRUE);
g_free(keylist);
g_free(file);
return TRUE;
}
if (ws_write(fd, keylist->data, strlen(keylist->data)) < 0) {
write_failure_alert_box(file, errno);
ws_close(fd);
g_free(keylist);
g_free(file);
return TRUE;
}
if (ws_close(fd) < 0) {
write_failure_alert_box(file, errno);
g_free(keylist);
g_free(file);
return TRUE;
}
/* Get rid of the dialog box */
g_free(keylist);
g_free(file);
return TRUE;
}
/* Launch the dialog box to put up the file selection box etc */
#ifdef _WIN32
void
savesslkeys_cb(GtkWidget * w _U_, gpointer data _U_)
{
win32_export_sslkeys_file(GDK_WINDOW_HWND(top_level->window));
return;
}
#else
void
savesslkeys_cb(GtkWidget * w _U_, gpointer data _U_)
{
gchar *label;
GtkWidget *dlg_lb;
guint keylist_len;
keylist_len = g_hash_table_size(ssl_session_hash);
/* don't show up the dialog, if no data has to be saved */
if (keylist_len==0) {
/* shouldn't happen as the menu item should have been greyed out */
simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "There are no SSL Session Keys to save!");
return;
}
/*
* Build the dialog box we need.
*/
savesslkeys_dlg = file_selection_new("Wireshark: Export SSL Session Keys", FILE_SELECTION_SAVE);
#if GTK_CHECK_VERSION(2,8,0)
gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(savesslkeys_dlg), TRUE);
#endif
/* label */
label = g_strdup_printf("Will save %u SSL Session %s to specified file.",
keylist_len, plurality(keylist_len, "key", "keys"));
dlg_lb = gtk_label_new(label);
g_free(label);
file_selection_set_extra_widget(savesslkeys_dlg, dlg_lb);
gtk_widget_show(dlg_lb);
g_signal_connect(savesslkeys_dlg, "destroy", G_CALLBACK(savesslkeys_dlg_destroy_cb), NULL);
/* "Run" the GtkFileChooserDialog. */
/* Upon exit: If "Accept" run the OK callback. */
/* If the OK callback returns with a FALSE status, re-run the dialog.*/
/* If not accept (ie: cancel) destroy the window. */
/* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
/* return with a TRUE status so that the dialog window will be destroyed. */
/* Trying to re-run the dialog after popping up an alert box will not work */
/* since the user will not be able to dismiss the alert box. */
/* The (somewhat unfriendly) effect: the user must re-invoke the */
/* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
/* */
/* ToDo: use GtkFileChooserWidget in a dialog window instead of */
/* GtkFileChooserDialog. */
while (gtk_dialog_run(GTK_DIALOG(savesslkeys_dlg)) == GTK_RESPONSE_ACCEPT) {
if (savesslkeys_save_clicked_cb(NULL, savesslkeys_dlg)) {
break; /* we're done */
}
}
window_destroy(savesslkeys_dlg);
}
#endif

43
gtk/export_sslkeys.h Normal file
View File

@ -0,0 +1,43 @@
/* export_sslkeys.h
*
* $Id$
*
* Export SSL Session Keys dialog
* by Sake Blok <sake@euronet.nl> (20110526)
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* 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
* of the License, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __EXPORT_SSLKEYS_H__
#define __EXPORT_SSLKEYS_H__
/** Callback for "Export SSL Session Keys" operation.
*
* @param w unused
* @param data unused
*/
extern void savesslkeys_cb(GtkWidget * w, gpointer data);
/** Dump the SSL Session Keys to a StringInfo string
*
* @param session_hash contains all the SSL Session Keys
*/
extern StringInfo* ssl_export_sessions(GHashTable *session_hash);
#endif /* __MAIN_PROTO_DRAW_H__ */

View File

@ -88,6 +88,8 @@
#include "gtk/firewall_dlg.h"
#include "gtk/macros_dlg.h"
#include "gtk/export_object.h"
#include "epan/dissectors/packet-ssl-utils.h"
#include "gtk/export_sslkeys.h"
#include "gtk/gui_stat_menu.h"
#include "gtk/main.h"
#include "gtk/menus.h"
@ -1434,6 +1436,7 @@ static const GtkActionEntry main_menu_bar_entries[] = {
NULL, NULL, G_CALLBACK(export_pdml_cmd_cb) },
#endif /* _WIN32 */
{ "/File/Export/SelectedPacketBytes", NULL, "Selected Packet _Bytes...", "<control>H", NULL, G_CALLBACK(savehex_cb) },
{ "/File/Export/SslSessionKeys", NULL, "SSL Session Keys...", NULL, NULL, G_CALLBACK(savesslkeys_cb) },
{ "/File/Export/Objects", NULL, "Objects", NULL, NULL, NULL },
{ "/File/Export/Objects/HTTP", NULL, "_HTTP", NULL, NULL, G_CALLBACK(eo_http_cb) },
{ "/File/Export/Objects/DICOM", NULL, "_DICOM", NULL, NULL, G_CALLBACK(eo_dicom_cb) },
@ -1876,6 +1879,8 @@ static GtkItemFactoryEntry menu_items[] =
#endif
{"/File/Export/Selected Packet _Bytes...", "<control>H", GTK_MENU_FUNC(savehex_cb),
0, NULL, NULL,},
{"/File/Export/SSL Session Keys...", NULL, GTK_MENU_FUNC(savesslkeys_cb),
0, NULL, NULL,},
{"/File/Export/_Objects/_HTTP", NULL, GTK_MENU_FUNC(eo_http_cb), 0, NULL, NULL,},
{"/File/Export/_Objects/_DICOM", NULL, GTK_MENU_FUNC(eo_dicom_cb), 0, NULL, NULL,},
{"/File/Export/_Objects/_SMB", NULL, GTK_MENU_FUNC(eo_smb_cb), 0, NULL, NULL,},

View File

@ -138,3 +138,10 @@ FONT 8, "MS Shell Dlg"
{
LTEXT "-", EWFD_EXPORTRAW_ST, 5, 5, 180, 8
}
WIRESHARK_EXPORTSSLKEYSFILENAME_TEMPLATE DIALOG 0, 0, 200, 18
STYLE WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | DS_3DLOOK | DS_CONTROL
FONT 8, "MS Shell Dlg"
{
LTEXT "-", EWFD_EXPORTSSLKEYS_ST, 5, 5, 180, 8
}

View File

@ -44,6 +44,8 @@
#include "epan/filesystem.h"
#include "epan/addr_resolv.h"
#include "epan/prefs.h"
#include "epan/dissectors/packet-ssl.h"
#include "epan/dissectors/packet-ssl-utils.h"
#include "wsutil/file_util.h"
#include "wsutil/unicode-utils.h"
@ -63,6 +65,7 @@
#include "gtk/capture_dlg.h"
#include "win32/file_dlg_win32.h"
#include "gtk/help_dlg.h"
#include "gtk/export_sslkeys.h"
typedef enum {
merge_append,
@ -90,6 +93,12 @@ typedef enum {
#define FILE_RAW_DEFAULT 1
#define FILE_TYPES_SSLKEYS \
_T("SSL Session Keys (*.keys)\0") _T("*.keys\0") \
_T("All Files (*.*)\0") _T("*.*\0")
#define FILE_SSLKEYS_DEFAULT 1
#define FILE_TYPES_COLOR \
_T("Text Files (*.txt)\0") _T("*.txt\0") \
_T("All Files (*.*)\0") _T("*.*\0")
@ -106,12 +115,14 @@ static UINT CALLBACK save_as_file_hook_proc(HWND of_hwnd, UINT ui_msg, WPARAM w_
static UINT CALLBACK merge_file_hook_proc(HWND mf_hwnd, UINT ui_msg, WPARAM w_param, LPARAM l_param);
static UINT CALLBACK export_file_hook_proc(HWND of_hwnd, UINT ui_msg, WPARAM w_param, LPARAM l_param);
static UINT CALLBACK export_raw_file_hook_proc(HWND of_hwnd, UINT ui_msg, WPARAM w_param, LPARAM l_param);
static UINT CALLBACK export_sslkeys_file_hook_proc(HWND of_hwnd, UINT ui_msg, WPARAM w_param, LPARAM l_param);
#else
static UINT_PTR CALLBACK open_file_hook_proc(HWND of_hwnd, UINT ui_msg, WPARAM w_param, LPARAM l_param);
static UINT_PTR CALLBACK save_as_file_hook_proc(HWND of_hwnd, UINT ui_msg, WPARAM w_param, LPARAM l_param);
static UINT_PTR CALLBACK merge_file_hook_proc(HWND mf_hwnd, UINT ui_msg, WPARAM w_param, LPARAM l_param);
static UINT_PTR CALLBACK export_file_hook_proc(HWND of_hwnd, UINT ui_msg, WPARAM w_param, LPARAM l_param);
static UINT_PTR CALLBACK export_raw_file_hook_proc(HWND of_hwnd, UINT ui_msg, WPARAM w_param, LPARAM l_param);
static UINT_PTR CALLBACK export_sslkeys_file_hook_proc(HWND of_hwnd, UINT ui_msg, WPARAM w_param, LPARAM l_param);
#endif /* (_MSC_VER <= 1200) */
static void range_update_dynamics(HWND sf_hwnd, packet_range_t *range);
@ -722,6 +733,98 @@ win32_export_raw_file(HWND h_wnd) {
}
}
void
win32_export_sslkeys_file(HWND h_wnd) {
OPENFILENAME *ofn;
TCHAR file_name[MAX_PATH] = _T("");
char *dirname;
StringInfo *keylist;
char *file_name8;
int fd;
int ofnsize;
int keylist_size;
#if (_MSC_VER >= 1500)
OSVERSIONINFO osvi;
#endif
keylist_size = g_hash_table_size(ssl_session_hash);
if (keylist_size==0) {
/* This shouldn't happen */
simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "No SSL Session Keys to export.");
return;
}
/* see OPENFILENAME comment in win32_open_file */
#if (_MSC_VER >= 1500)
ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&osvi);
if (osvi.dwMajorVersion >= 5) {
ofnsize = sizeof(OPENFILENAME);
} else {
ofnsize = OPENFILENAME_SIZE_VERSION_400;
}
#else
ofnsize = sizeof(OPENFILENAME) + 12;
#endif
ofn = g_malloc0(ofnsize);
ofn->lStructSize = ofnsize;
ofn->hwndOwner = h_wnd;
#if (_MSC_VER <= 1200)
ofn->hInstance = (HINSTANCE) GetWindowLong(h_wnd, GWL_HINSTANCE);
#else
ofn->hInstance = (HINSTANCE) GetWindowLongPtr(h_wnd, GWLP_HINSTANCE);
#endif
ofn->lpstrFilter = FILE_TYPES_SSLKEYS;
ofn->lpstrCustomFilter = NULL;
ofn->nMaxCustFilter = 0;
ofn->nFilterIndex = FILE_SSLKEYS_DEFAULT;
ofn->lpstrFile = file_name;
ofn->nMaxFile = MAX_PATH;
ofn->lpstrFileTitle = NULL;
ofn->nMaxFileTitle = 0;
ofn->lpstrInitialDir = utf_8to16(get_last_open_dir());
ofn->lpstrTitle = _T("Wireshark: Export SSL Session Keys");
ofn->Flags = OFN_ENABLESIZING | OFN_ENABLETEMPLATE | OFN_EXPLORER |
OFN_NOCHANGEDIR | OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY |
OFN_PATHMUSTEXIST | OFN_ENABLEHOOK | OFN_SHOWHELP;
ofn->lpstrDefExt = NULL;
ofn->lCustData = keylist_size;
ofn->lpfnHook = export_sslkeys_file_hook_proc;
ofn->lpTemplateName = _T("WIRESHARK_EXPORTSSLKEYSFILENAME_TEMPLATE");
if (GetSaveFileName(ofn)) {
g_free( (void *) ofn);
file_name8 = utf_16to8(file_name);
keylist = ssl_export_sessions(ssl_session_hash);
fd = ws_open(file_name8, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666);
if (fd == -1) {
open_failure_alert_box(file_name8, errno, TRUE);
g_free(keylist);
return;
}
if (write(fd, keylist->data, strlen(keylist->data)) < 0) {
write_failure_alert_box(file_name8, errno);
close(fd);
g_free(keylist);
return;
}
if (close(fd) < 0) {
write_failure_alert_box(file_name8, errno);
g_free(keylist);
return;
}
/* Save the directory name for future file dialogs. */
dirname = get_dirname(file_name8); /* Overwrites cf_name */
set_last_open_dir(dirname);
} else {
g_free( (void *) ofn);
}
g_free(keylist);
}
void
win32_export_color_file(HWND h_wnd, gpointer filter_list) {
OPENFILENAME *ofn;
@ -1958,6 +2061,38 @@ export_raw_file_hook_proc(HWND ef_hwnd, UINT msg, WPARAM w_param, LPARAM l_param
return 0;
}
#if (_MSC_VER <= 1200)
static UINT CALLBACK
#else
static UINT_PTR CALLBACK
#endif
export_sslkeys_file_hook_proc(HWND ef_hwnd, UINT msg, WPARAM w_param, LPARAM l_param) {
HWND cur_ctrl;
OPENFILENAME *ofnp = (OPENFILENAME *) l_param;
TCHAR sslkeys_msg[STATIC_LABEL_CHARS];
OFNOTIFY *notify = (OFNOTIFY *) l_param;
switch(msg) {
case WM_INITDIALOG:
_snwprintf(sslkeys_msg, STATIC_LABEL_CHARS, _T("%d SSL Session Key%s will be written"),
ofnp->lCustData, utf_8to16(plurality(ofnp->lCustData, "", "s")));
cur_ctrl = GetDlgItem(ef_hwnd, EWFD_EXPORTSSLKEYS_ST);
SetWindowText(cur_ctrl, sslkeys_msg);
break;
case WM_NOTIFY:
switch (notify->hdr.code) {
case CDN_HELP:
topic_cb(NULL, HELP_EXPORT_BYTES_WIN32_DIALOG);
break;
default:
break;
}
default:
break;
}
return 0;
}
/*
* Editor modelines
*

View File

@ -67,6 +67,12 @@ void win32_export_file (HWND h_wnd, export_type_e export_type);
*/
void win32_export_raw_file (HWND h_wnd);
/** Open the "Export SSL Session Keys" dialog box.
*
* @param h_wnd HWND of the parent window.
*/
void win32_export_sslkeys_file (HWND h_wnd);
/** Open the "Export Color Filters" dialog box
*
* @param h_wnd HWND of the parent window
@ -138,6 +144,9 @@ void file_set_save_marked_sensitive();
/* Export raw dialog defines. */
#define EWFD_EXPORTRAW_ST 1000
/* Export SSL Session Keys dialog defines. */
#define EWFD_EXPORTSSLKEYS_ST 1000
/* Merge dialog defines. Overlays Open dialog defines above. */
#define EWFD_MERGE_PREPEND_BTN 1050
#define EWFD_MERGE_CHRONO_BTN 1051