diff --git a/cli/cmd/gnutls.go b/cli/cmd/gnutls.go index cf3bac48e..5ddc9ea51 100644 --- a/cli/cmd/gnutls.go +++ b/cli/cmd/gnutls.go @@ -18,6 +18,8 @@ package cmd import ( + "strings" + "github.com/gojue/ecapture/user/config" "github.com/gojue/ecapture/user/module" "github.com/spf13/cobra" @@ -35,6 +37,8 @@ ecapture gnutls ecapture gnutls --hex --pid=3423 ecapture gnutls -l save.log --pid=3423 ecapture gnutls --gnutls=/lib/x86_64-linux-gnu/libgnutls.so +ecapture gnutls -m keylog -k ecapture_gnutls_key.og --ssl_version=3.7.9 +ecapture gnutls -m pcap --pcapfile save.pcapng -i eth0 --gnutls=/lib/x86_64-linux-gnu/libgnutls.so tcp port 443 `, Run: gnuTlsCommandFunc, } @@ -42,10 +46,18 @@ ecapture gnutls --gnutls=/lib/x86_64-linux-gnu/libgnutls.so func init() { //opensslCmd.PersistentFlags().StringVar(&gc.Curlpath, "wget", "", "wget file path, default: /usr/bin/wget. (Deprecated)") gnutlsCmd.PersistentFlags().StringVar(&gc.Gnutls, "gnutls", "", "libgnutls.so file path, will automatically find it from curl default.") + gnutlsCmd.PersistentFlags().StringVarP(&gc.Model, "model", "m", "text", "capture model, such as : text, pcap/pcapng, key/keylog") + gnutlsCmd.PersistentFlags().StringVarP(&gc.KeylogFile, "keylogfile", "k", "ecapture_gnutls_key.log", "The file stores SSL/TLS keys, and eCapture captures these keys during encrypted traffic communication and saves them to the file.") + gnutlsCmd.PersistentFlags().StringVarP(&gc.PcapFile, "pcapfile", "w", "save.pcapng", "write the raw packets to file as pcapng format.") + gnutlsCmd.PersistentFlags().StringVarP(&gc.Ifname, "ifname", "i", "", "(TC Classifier) Interface name on which the probe will be attached.") + gnutlsCmd.PersistentFlags().StringVar(&gc.SslVersion, "ssl_version", "", "GnuTLS version, e.g: --ssl_version=\"3.7.9\"") rootCmd.AddCommand(gnutlsCmd) } // gnuTlsCommandFunc executes the "bash" command. func gnuTlsCommandFunc(command *cobra.Command, args []string) { + if gc.PcapFilter == "" && len(args) != 0 { + gc.PcapFilter = strings.Join(args, " ") + } runModule(module.ModuleNameGnutls, gc) } diff --git a/kern/gnutls_kern.c b/kern/gnutls.h similarity index 99% rename from kern/gnutls_kern.c rename to kern/gnutls.h index cb57e7276..2b2195dbb 100644 --- a/kern/gnutls_kern.c +++ b/kern/gnutls.h @@ -13,6 +13,7 @@ // limitations under the License. #include "ecapture.h" +#include "tc.h" enum ssl_data_event_type { kSSLRead, kSSLWrite }; diff --git a/kern/gnutls_3_6_12_kern.c b/kern/gnutls_3_6_12_kern.c new file mode 100644 index 000000000..ab42b2d04 --- /dev/null +++ b/kern/gnutls_3_6_12_kern.c @@ -0,0 +1,45 @@ +#ifndef ECAPTURE_GNUTLS_3_6_12_KERN_H +#define ECAPTURE_GNUTLS_3_6_12_KERN_H + +// version 3.6.12, 3.6.13 + +// gnutls_session_int->security_parameters +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS 0x0 + +// gnutls_session_int->security_parameters.prf +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_PRF 0x18 + +// mac_entry_st->id +#define MAC_ENTRY_ST_ID 0x18 + +// gnutls_session_int->security_parameters.client_random +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_CLIENT_RANDOM 0x50 + +// gnutls_session_int->security_parameters.master_secret +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_MASTER_SECRET 0x20 + +// gnutls_session_int->key.proto.tls13.hs_ckey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_HS_CKEY 0x14d4 + +// gnutls_session_int->key.proto.tls13.hs_skey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_HS_SKEY 0x1514 + +// gnutls_session_int->key.proto.tls13.ap_ckey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_CKEY 0x1554 + +// gnutls_session_int->key.proto.tls13.ap_skey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_SKEY 0x1594 + +// gnutls_session_int->key.proto.tls13.ap_expkey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_EXPKEY 0x15d4 + +// security_parameters_st->pversion +#define SECURITY_PARAMETERS_ST_PVERSION 0xf8 + +// version_entry_st->id +#define VERSION_ENTRY_ST_ID 0x8 + +#include "gnutls.h" +#include "gnutls_masterkey.h" + +#endif diff --git a/kern/gnutls_3_6_14_kern.c b/kern/gnutls_3_6_14_kern.c new file mode 100644 index 000000000..09f2479f0 --- /dev/null +++ b/kern/gnutls_3_6_14_kern.c @@ -0,0 +1,45 @@ +#ifndef ECAPTURE_GNUTLS_3_6_14_KERN_H +#define ECAPTURE_GNUTLS_3_6_14_KERN_H + +// version 3.6.14, 3.6.15, 3.6.16 + +// gnutls_session_int->security_parameters +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS 0x0 + +// gnutls_session_int->security_parameters.prf +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_PRF 0x18 + +// mac_entry_st->id +#define MAC_ENTRY_ST_ID 0x18 + +// gnutls_session_int->security_parameters.client_random +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_CLIENT_RANDOM 0x50 + +// gnutls_session_int->security_parameters.master_secret +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_MASTER_SECRET 0x20 + +// gnutls_session_int->key.proto.tls13.hs_ckey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_HS_CKEY 0x17e4 + +// gnutls_session_int->key.proto.tls13.hs_skey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_HS_SKEY 0x1824 + +// gnutls_session_int->key.proto.tls13.ap_ckey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_CKEY 0x1864 + +// gnutls_session_int->key.proto.tls13.ap_skey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_SKEY 0x18a4 + +// gnutls_session_int->key.proto.tls13.ap_expkey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_EXPKEY 0x18e4 + +// security_parameters_st->pversion +#define SECURITY_PARAMETERS_ST_PVERSION 0xf8 + +// version_entry_st->id +#define VERSION_ENTRY_ST_ID 0x8 + +#include "gnutls.h" +#include "gnutls_masterkey.h" + +#endif diff --git a/kern/gnutls_3_7_0_kern.c b/kern/gnutls_3_7_0_kern.c new file mode 100644 index 000000000..fd08129ac --- /dev/null +++ b/kern/gnutls_3_7_0_kern.c @@ -0,0 +1,45 @@ +#ifndef ECAPTURE_GNUTLS_3_7_0_KERN_H +#define ECAPTURE_GNUTLS_3_7_0_KERN_H + +// version 3.7.0, 3.7.1, 3.7.2 + +// gnutls_session_int->security_parameters +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS 0x0 + +// gnutls_session_int->security_parameters.prf +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_PRF 0x18 + +// mac_entry_st->id +#define MAC_ENTRY_ST_ID 0x18 + +// gnutls_session_int->security_parameters.client_random +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_CLIENT_RANDOM 0x50 + +// gnutls_session_int->security_parameters.master_secret +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_MASTER_SECRET 0x20 + +// gnutls_session_int->key.proto.tls13.hs_ckey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_HS_CKEY 0x1804 + +// gnutls_session_int->key.proto.tls13.hs_skey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_HS_SKEY 0x1844 + +// gnutls_session_int->key.proto.tls13.ap_ckey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_CKEY 0x1884 + +// gnutls_session_int->key.proto.tls13.ap_skey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_SKEY 0x18c4 + +// gnutls_session_int->key.proto.tls13.ap_expkey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_EXPKEY 0x1904 + +// security_parameters_st->pversion +#define SECURITY_PARAMETERS_ST_PVERSION 0xf8 + +// version_entry_st->id +#define VERSION_ENTRY_ST_ID 0x8 + +#include "gnutls.h" +#include "gnutls_masterkey.h" + +#endif diff --git a/kern/gnutls_3_7_3_kern.c b/kern/gnutls_3_7_3_kern.c new file mode 100644 index 000000000..d362ac6e2 --- /dev/null +++ b/kern/gnutls_3_7_3_kern.c @@ -0,0 +1,45 @@ +#ifndef ECAPTURE_GNUTLS_3_7_3_KERN_H +#define ECAPTURE_GNUTLS_3_7_3_KERN_H + +// version 3.7.3, 3.7.4, 3.7.5, 3.7.6 + +// gnutls_session_int->security_parameters +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS 0x0 + +// gnutls_session_int->security_parameters.prf +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_PRF 0x18 + +// mac_entry_st->id +#define MAC_ENTRY_ST_ID 0x18 + +// gnutls_session_int->security_parameters.client_random +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_CLIENT_RANDOM 0x50 + +// gnutls_session_int->security_parameters.master_secret +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_MASTER_SECRET 0x20 + +// gnutls_session_int->key.proto.tls13.hs_ckey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_HS_CKEY 0x180c + +// gnutls_session_int->key.proto.tls13.hs_skey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_HS_SKEY 0x184c + +// gnutls_session_int->key.proto.tls13.ap_ckey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_CKEY 0x188c + +// gnutls_session_int->key.proto.tls13.ap_skey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_SKEY 0x18cc + +// gnutls_session_int->key.proto.tls13.ap_expkey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_EXPKEY 0x190c + +// security_parameters_st->pversion +#define SECURITY_PARAMETERS_ST_PVERSION 0xf8 + +// version_entry_st->id +#define VERSION_ENTRY_ST_ID 0x8 + +#include "gnutls.h" +#include "gnutls_masterkey.h" + +#endif diff --git a/kern/gnutls_3_7_7_kern.c b/kern/gnutls_3_7_7_kern.c new file mode 100644 index 000000000..19dc1fcfa --- /dev/null +++ b/kern/gnutls_3_7_7_kern.c @@ -0,0 +1,46 @@ +#ifndef ECAPTURE_GNUTLS_3_7_7_KERN_H +#define ECAPTURE_GNUTLS_3_7_7_KERN_H + +// version 3.7.7, 3.7.8, 3.7.9, 3.7.10, 3.7.11 +// version 3.8.0, 3.8.1, 3.8.2, 3.8.3 + +// gnutls_session_int->security_parameters +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS 0x0 + +// gnutls_session_int->security_parameters.prf +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_PRF 0x18 + +// mac_entry_st->id +#define MAC_ENTRY_ST_ID 0x18 + +// gnutls_session_int->security_parameters.client_random +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_CLIENT_RANDOM 0x50 + +// gnutls_session_int->security_parameters.master_secret +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_MASTER_SECRET 0x20 + +// gnutls_session_int->key.proto.tls13.hs_ckey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_HS_CKEY 0x1794 + +// gnutls_session_int->key.proto.tls13.hs_skey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_HS_SKEY 0x17d4 + +// gnutls_session_int->key.proto.tls13.ap_ckey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_CKEY 0x1814 + +// gnutls_session_int->key.proto.tls13.ap_skey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_SKEY 0x1854 + +// gnutls_session_int->key.proto.tls13.ap_expkey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_EXPKEY 0x1894 + +// security_parameters_st->pversion +#define SECURITY_PARAMETERS_ST_PVERSION 0xf8 + +// version_entry_st->id +#define VERSION_ENTRY_ST_ID 0x8 + +#include "gnutls.h" +#include "gnutls_masterkey.h" + +#endif diff --git a/kern/gnutls_3_8_4_kern.c b/kern/gnutls_3_8_4_kern.c new file mode 100644 index 000000000..6bebae359 --- /dev/null +++ b/kern/gnutls_3_8_4_kern.c @@ -0,0 +1,45 @@ +#ifndef ECAPTURE_GNUTLS_3_8_4_KERN_H +#define ECAPTURE_GNUTLS_3_8_4_KERN_H + +// version 3.8.4, 3.8.5, 3.8.6 + +// gnutls_session_int->security_parameters +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS 0x0 + +// gnutls_session_int->security_parameters.prf +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_PRF 0x18 + +// mac_entry_st->id +#define MAC_ENTRY_ST_ID 0x18 + +// gnutls_session_int->security_parameters.client_random +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_CLIENT_RANDOM 0x50 + +// gnutls_session_int->security_parameters.master_secret +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_MASTER_SECRET 0x20 + +// gnutls_session_int->key.proto.tls13.hs_ckey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_HS_CKEY 0x17dc + +// gnutls_session_int->key.proto.tls13.hs_skey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_HS_SKEY 0x181c + +// gnutls_session_int->key.proto.tls13.ap_ckey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_CKEY 0x185c + +// gnutls_session_int->key.proto.tls13.ap_skey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_SKEY 0x189c + +// gnutls_session_int->key.proto.tls13.ap_expkey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_EXPKEY 0x18dc + +// security_parameters_st->pversion +#define SECURITY_PARAMETERS_ST_PVERSION 0xf8 + +// version_entry_st->id +#define VERSION_ENTRY_ST_ID 0x8 + +#include "gnutls.h" +#include "gnutls_masterkey.h" + +#endif diff --git a/kern/gnutls_3_8_7_kern.c b/kern/gnutls_3_8_7_kern.c new file mode 100644 index 000000000..7ab3eca36 --- /dev/null +++ b/kern/gnutls_3_8_7_kern.c @@ -0,0 +1,45 @@ +#ifndef ECAPTURE_GNUTLS_3_8_7_KERN_H +#define ECAPTURE_GNUTLS_3_8_7_KERN_H + +// version 3.8.7 + +// gnutls_session_int->security_parameters +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS 0x0 + +// gnutls_session_int->security_parameters.prf +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_PRF 0x18 + +// mac_entry_st->id +#define MAC_ENTRY_ST_ID 0x18 + +// gnutls_session_int->security_parameters.client_random +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_CLIENT_RANDOM 0x50 + +// gnutls_session_int->security_parameters.master_secret +#define GNUTLS_SESSION_INT_SECURITY_PARAMETERS_MASTER_SECRET 0x20 + +// gnutls_session_int->key.proto.tls13.hs_ckey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_HS_CKEY 0x19d4 + +// gnutls_session_int->key.proto.tls13.hs_skey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_HS_SKEY 0x1a14 + +// gnutls_session_int->key.proto.tls13.ap_ckey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_CKEY 0x1a54 + +// gnutls_session_int->key.proto.tls13.ap_skey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_SKEY 0x1a94 + +// gnutls_session_int->key.proto.tls13.ap_expkey +#define GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_EXPKEY 0x1ad4 + +// security_parameters_st->pversion +#define SECURITY_PARAMETERS_ST_PVERSION 0xf8 + +// version_entry_st->id +#define VERSION_ENTRY_ST_ID 0x8 + +#include "gnutls.h" +#include "gnutls_masterkey.h" + +#endif diff --git a/kern/gnutls_masterkey.h b/kern/gnutls_masterkey.h new file mode 100644 index 000000000..db8975b8c --- /dev/null +++ b/kern/gnutls_masterkey.h @@ -0,0 +1,317 @@ +// Author: yuweizzz . +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Ref: +// https://github.com/gnutls/gnutls/blob/3.7.9/lib/gnutls_int.h +// +// typedef struct gnutls_session_int *gnutls_session_t; +// struct gnutls_session_int { +// security_parameters_st security_parameters; +// record_parameters_st *record_parameters[MAX_EPOCH_INDEX]; +// internals_st internals; +// gnutls_key_st key; +// }; +// +// gnutls_session_int --> security_parameters_st +// struct security_parameters_st { +// // ignore +// // ... +// const mac_entry_st *prf; +// uint8_t master_secret[GNUTLS_MASTER_SIZE]; +// uint8_t client_random[GNUTLS_RANDOM_SIZE]; +// // ignore +// // ... +// const version_entry_st *pversion; +// }; +// +// gnutls_session_int --> security_parameters_st --> mac_entry_st +// typedef struct mac_entry_st { +// // ignore +// // ... +// gnutls_mac_algorithm_t id; +// // ignore +// // ... +// } mac_entry_st; +// +// gnutls_session_int --> security_parameters_st -> version_entry_st +// typedef struct { +// // ignore +// // ... +// gnutls_protocol_t id; /* gnutls internal version number */ +// // ignore +// // ... +// } version_entry_st; +// +// gnutls_session_int --> gnutls_key_st +// struct gnutls_key_st { +// // ignore +// // ... +// union { +// struct { +// // ignore +// // ... +// uint8_t hs_ckey[MAX_HASH_SIZE]; /* client_hs_traffic_secret */ +// uint8_t hs_skey[MAX_HASH_SIZE]; /* server_hs_traffic_secret */ +// uint8_t ap_ckey[MAX_HASH_SIZE]; /* client_ap_traffic_secret */ +// uint8_t ap_skey[MAX_HASH_SIZE]; /* server_ap_traffic_secret */ +// uint8_t ap_expkey[MAX_HASH_SIZE]; /* {early_,}exporter_master_secret */ +// // ignore +// // ... +// } tls13; /* tls1.3 */ +// +// /* Follow the SSL3.0 and TLS1.2 key exchanges */ +// struct { +// // ignore +// // ... +// } tls12; /* from ssl3.0 to tls12 */ +// } proto; +// // ignore +// // ... +// }; +// + +#define GNUTLS_RANDOM_SIZE 32 +#define GNUTLS_MASTER_SIZE 48 +#define MAX_HASH_SIZE 64 + +struct gnutls_mastersecret_st { + u32 version; + /* from ssl3.0 to tls1.2 */ + u8 client_random[GNUTLS_RANDOM_SIZE]; + u8 master_secret[GNUTLS_MASTER_SIZE]; + + /* tls1.3 */ + u32 cipher_id; + u8 client_handshake_secret[MAX_HASH_SIZE]; + u8 server_handshake_secret[MAX_HASH_SIZE]; + u8 client_traffic_secret[MAX_HASH_SIZE]; + u8 server_traffic_secret[MAX_HASH_SIZE]; + u8 exporter_master_secret[MAX_HASH_SIZE]; +}; + +// #define GNUTLS_MAC_SHA256 6 +// #define GNUTLS_MAC_SHA384 7 + +#define GNUTLS_SSL3 1 +#define GNUTLS_TLS1_0 2 +#define GNUTLS_TLS1 GNUTLS_TLS1_0 +#define GNUTLS_TLS1_1 3 +#define GNUTLS_TLS1_2 4 +#define GNUTLS_TLS1_3 5 +#define GNUTLS_DTLS1_0 201 +#define GNUTLS_DTLS1_2 202 + +/////////////////////////BPF MAPS //////////////////////////////// + +// bpf map +struct { + __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); + __uint(key_size, sizeof(u32)); + __uint(value_size, sizeof(u32)); + __uint(max_entries, 1024); +} mastersecret_gnutls_events SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __type(key, u64); + __type(value, u64); + __uint(max_entries, 1024); +} gnutls_session_maps SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __type(key, u64); + __type(value, struct gnutls_mastersecret_st); + __uint(max_entries, 2048); +} bpf_context SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __type(key, u32); + __type(value, struct gnutls_mastersecret_st); + __uint(max_entries, 1); +} bpf_context_gen SEC(".maps"); + +/////////////////////////COMMON FUNCTIONS //////////////////////////////// +// 这个函数用来规避512字节栈空间限制,通过在堆上创建内存的方式,避开限制 +static __always_inline struct gnutls_mastersecret_st *make_event() { + u32 key_gen = 0; + struct gnutls_mastersecret_st *bpf_ctx = bpf_map_lookup_elem(&bpf_context_gen, &key_gen); + if (!bpf_ctx) return 0; + u64 id = bpf_get_current_pid_tgid(); + bpf_map_update_elem(&bpf_context, &id, bpf_ctx, BPF_ANY); + return bpf_map_lookup_elem(&bpf_context, &id); +} + +/////////////////////////BPF FUNCTIONS //////////////////////////////// +SEC("uprobe/gnutls_handshake") +int uprobe_gnutls_master_key(struct pt_regs *ctx) { + u64 current_pid_tgid = bpf_get_current_pid_tgid(); + u32 pid = current_pid_tgid >> 32; + u64 current_uid_gid = bpf_get_current_uid_gid(); + u32 uid = current_uid_gid; +#ifndef KERNEL_LESS_5_2 + // if target_ppid is 0 then we target all pids + if (target_pid != 0 && target_pid != pid) { + return 0; + } + if (target_uid != 0 && target_uid != uid) { + return 0; + } +#endif + u64 gnutls_session_addr = (u64)PT_REGS_PARM1(ctx); + bpf_map_update_elem(&gnutls_session_maps, ¤t_pid_tgid, &gnutls_session_addr, BPF_ANY); + debug_bpf_printk("gnutls uprobe/gnutls_handshake PID: %d, gnutls_session_addr: %d\n", pid, gnutls_session_addr); + return 0; +} + +SEC("uretprobe/gnutls_handshake") +int uretprobe_gnutls_master_key(struct pt_regs *ctx) { + u64 current_pid_tgid = bpf_get_current_pid_tgid(); + u32 pid = current_pid_tgid >> 32; + u64 current_uid_gid = bpf_get_current_uid_gid(); + u32 uid = current_uid_gid; + +#ifndef KERNEL_LESS_5_2 + // if target_ppid is 0 then we target all pids + if (target_pid != 0 && target_pid != pid) { + return 0; + } + if (target_uid != 0 && target_uid != uid) { + return 0; + } +#endif + + u8 handshake_return = (u8)PT_REGS_RC(ctx); + if (handshake_return != 0) { + // handshake failed + debug_bpf_printk("gnutls uretprobe/gnutls_handshake PID: %d, handshake failed, ret: %d\n", pid, handshake_return); + return 0; + } + debug_bpf_printk("gnutls uretprobe/gnutls_handshake PID: %d\n", pid); + + u64 *gnutls_session_addr_ptr = bpf_map_lookup_elem(&gnutls_session_maps, ¤t_pid_tgid); + if (!gnutls_session_addr_ptr) { + debug_bpf_printk("gnutls uretprobe/gnutls_handshake, lookup for gnutls_session_addr failed\n"); + return 0; + } + + u64 gnutls_session_addr = (u64) *gnutls_session_addr_ptr; + debug_bpf_printk("gnutls uretprobe/gnutls_handshake, gnutls_session_addr: %d\n", gnutls_session_addr); + + // ssl_version + u64 pversion_addr; + int ret = bpf_probe_read_user(&pversion_addr, sizeof(pversion_addr), + (void *)(gnutls_session_addr + GNUTLS_SESSION_INT_SECURITY_PARAMETERS + SECURITY_PARAMETERS_ST_PVERSION)); + if (ret) { + debug_bpf_printk("gnutls uretprobe/gnutls_handshake, get pversion_addr failed, ret: %d\n", ret); + return 0; + } + int ssl_version; + ret = bpf_probe_read_user(&ssl_version, sizeof(ssl_version), (void *)(pversion_addr + VERSION_ENTRY_ST_ID)); + if (ret) { + debug_bpf_printk("gnutls uretprobe/gnutls_handshake, get ssl_version failed, ret: %d\n", ret); + return 0; + } + debug_bpf_printk("ssl_version: %d\n", ssl_version); + + /* from ssl3.0 to tls1.2 */ + if ((ssl_version >= GNUTLS_SSL3 && ssl_version <= GNUTLS_TLS1_2) || + (ssl_version >= GNUTLS_DTLS1_0 && ssl_version <= GNUTLS_DTLS1_2)) { + struct gnutls_mastersecret_st *mastersecret = make_event(); + if (!mastersecret) { + debug_bpf_printk("gnutls uretprobe/gnutls_handshake, mastersecret is null\n"); + return 0; + } + mastersecret->version = ssl_version; + ret = bpf_probe_read_user(&mastersecret->client_random, sizeof(mastersecret->client_random), + (void *)(gnutls_session_addr + GNUTLS_SESSION_INT_SECURITY_PARAMETERS_CLIENT_RANDOM)); + if (ret) { + debug_bpf_printk("gnutls uretprobe/gnutls_handshake, get client_random failed, ret: %d\n", ret); + return 0; + } + ret = bpf_probe_read_user(&mastersecret->master_secret, sizeof(mastersecret->master_secret), + (void *)(gnutls_session_addr + GNUTLS_SESSION_INT_SECURITY_PARAMETERS_MASTER_SECRET)); + if (ret) { + debug_bpf_printk("gnutls uretprobe/gnutls_handshake, get master_secret failed, ret: %d\n", ret); + return 0; + } + bpf_perf_event_output(ctx, &mastersecret_gnutls_events, BPF_F_CURRENT_CPU, mastersecret, sizeof(struct gnutls_mastersecret_st)); + } + + // tls 1.3 + if (ssl_version == GNUTLS_TLS1_3) { + struct gnutls_mastersecret_st *mastersecret = make_event(); + if (!mastersecret) { + debug_bpf_printk("gnutls uretprobe/gnutls_handshake, mastersecret is null\n"); + return 0; + } + mastersecret->version = ssl_version; + // mac cipher id + u64 prf_addr; + ret = bpf_probe_read_user(&prf_addr, sizeof(prf_addr), + (void *)(gnutls_session_addr + GNUTLS_SESSION_INT_SECURITY_PARAMETERS_PRF)); + if (ret) { + debug_bpf_printk("gnutls uretprobe/gnutls_handshake, get prf failed, ret: %d\n", ret); + return 0; + } + int mac_cipher_id; + ret = bpf_probe_read_user(&mac_cipher_id, sizeof(mac_cipher_id), (void *)(prf_addr + MAC_ENTRY_ST_ID)); + if (ret) { + debug_bpf_printk("gnutls uretprobe/gnutls_handshake, get mac_cipher_id failed, ret: %d\n", ret); + return 0; + } + debug_bpf_printk("gnutls uretprobe/gnutls_handshake, get mac_cipher_id ret: %d\n", mac_cipher_id); + mastersecret->cipher_id = mac_cipher_id; + ret = bpf_probe_read_user(&mastersecret->client_random, sizeof(mastersecret->client_random), + (void *)(gnutls_session_addr + GNUTLS_SESSION_INT_SECURITY_PARAMETERS_CLIENT_RANDOM)); + if (ret) { + debug_bpf_printk("gnutls uretprobe/gnutls_handshake, get client_random failed, ret: %d\n", ret); + return 0; + } + ret = bpf_probe_read_user(&mastersecret->client_handshake_secret, sizeof(mastersecret->client_handshake_secret), + (void *)(gnutls_session_addr + GNUTLS_SESSION_INT_KEY_PROTO_TLS13_HS_CKEY)); + if (ret) { + debug_bpf_printk("gnutls uretprobe/gnutls_handshake, get client_handshake_secret failed, ret: %d\n", ret); + return 0; + } + ret = bpf_probe_read_user(&mastersecret->server_handshake_secret, sizeof(mastersecret->server_handshake_secret), + (void *)(gnutls_session_addr + GNUTLS_SESSION_INT_KEY_PROTO_TLS13_HS_SKEY)); + if (ret) { + debug_bpf_printk("gnutls uretprobe/gnutls_handshake, get server_handshake_secret failed, ret: %d\n", ret); + return 0; + } + ret = bpf_probe_read_user(&mastersecret->client_traffic_secret, sizeof(mastersecret->client_traffic_secret), + (void *)(gnutls_session_addr + GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_CKEY)); + if (ret) { + debug_bpf_printk("gnutls uretprobe/gnutls_handshake, get client_traffic_secret failed, ret: %d\n", ret); + return 0; + } + ret = bpf_probe_read_user(&mastersecret->server_traffic_secret, sizeof(mastersecret->server_traffic_secret), + (void *)(gnutls_session_addr + GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_SKEY)); + if (ret) { + debug_bpf_printk("gnutls uretprobe/gnutls_handshake, get server_traffic_secret failed, ret: %d\n", ret); + return 0; + } + ret = bpf_probe_read_user(&mastersecret->exporter_master_secret, sizeof(mastersecret->exporter_master_secret), + (void *)(gnutls_session_addr + GNUTLS_SESSION_INT_KEY_PROTO_TLS13_AP_EXPKEY)); + if (ret) { + debug_bpf_printk("gnutls uretprobe/gnutls_handshake, get exporter_master_secret failed, ret: %d\n", ret); + return 0; + } + bpf_perf_event_output(ctx, &mastersecret_gnutls_events, BPF_F_CURRENT_CPU, mastersecret, sizeof(struct gnutls_mastersecret_st)); + } + + return 0; +} diff --git a/user/config/config_gnutls.go b/user/config/config_gnutls.go index ded7bd7fd..787b86b5e 100644 --- a/user/config/config_gnutls.go +++ b/user/config/config_gnutls.go @@ -20,8 +20,14 @@ import "encoding/json" type GnutlsConfig struct { BaseConfig //Curl path string `json:"curlpath"` //curl的文件路径 - Gnutls string `json:"gnutls"` - ElfType uint8 // + Gnutls string `json:"gnutls"` + Model string `json:"model"` + PcapFile string `json:"pcapfile"` + KeylogFile string `json:"keylog"` + Ifname string `json:"ifname"` + PcapFilter string `json:"pcapfilter"` + SslVersion string `json:"sslversion"` + ElfType uint8 } func NewGnutlsConfig() *GnutlsConfig { @@ -30,6 +36,19 @@ func NewGnutlsConfig() *GnutlsConfig { return config } +func (gc *GnutlsConfig) checkModel() string { + var m string + switch gc.Model { + case TlsCaptureModelKeylog, TlsCaptureModelKey: + m = TlsCaptureModelKey + case TlsCaptureModelPcap, TlsCaptureModelPcapng: + m = TlsCaptureModelPcap + default: + m = TlsCaptureModelText + } + return m +} + func (gc *GnutlsConfig) Bytes() []byte { b, e := json.Marshal(gc) if e != nil { diff --git a/user/config/config_gnutls_linux.go b/user/config/config_gnutls_linux.go index 8290509c6..9ec5ffbe5 100644 --- a/user/config/config_gnutls_linux.go +++ b/user/config/config_gnutls_linux.go @@ -58,6 +58,6 @@ func (gc *GnutlsConfig) Check() error { } gc.ElfType = ElfTypeSo - + gc.Model = gc.checkModel() return nil } diff --git a/user/event/event_mastersecret_gnutls.go b/user/event/event_mastersecret_gnutls.go new file mode 100644 index 000000000..88fdfe3ba --- /dev/null +++ b/user/event/event_mastersecret_gnutls.go @@ -0,0 +1,107 @@ +// Author: yuweizzz . +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package event + +import ( + "bytes" + "encoding/binary" + "fmt" +) + +const ( + GnutlsMasterSize = 48 + GnutlsRandomSize = 32 + GnutlsMaxHashSize = 64 +) + +// mastersecret_gnutls_events +type MasterSecretGnutlsEvent struct { + eventType EventType + Version int32 `json:"version"` + ClientRandom [GnutlsRandomSize]byte `json:"clientRandom"` + MasterSecret [GnutlsMasterSize]byte `json:"masterSecret"` + CipherId int32 `json:"cipherId"` // PRF MAC + ClientHandshakeSecret [GnutlsMaxHashSize]byte `json:"clientHandshakeSecret"` + ServerHandshakeSecret [GnutlsMaxHashSize]byte `json:"serverHandshakeSecret"` + ClientTrafficSecret [GnutlsMaxHashSize]byte `json:"clientTrafficSecret"` + ServerTrafficSecret [GnutlsMaxHashSize]byte `json:"serverTrafficSecret"` + ExporterMasterSecret [GnutlsMaxHashSize]byte `json:"exporterMasterSecret"` + payload string +} + +func (mse *MasterSecretGnutlsEvent) Decode(payload []byte) (err error) { + buf := bytes.NewBuffer(payload) + if err = binary.Read(buf, binary.LittleEndian, &mse.Version); err != nil { + return + } + if err = binary.Read(buf, binary.LittleEndian, &mse.ClientRandom); err != nil { + return + } + if err = binary.Read(buf, binary.LittleEndian, &mse.MasterSecret); err != nil { + return + } + if err = binary.Read(buf, binary.LittleEndian, &mse.CipherId); err != nil { + return + } + if err = binary.Read(buf, binary.LittleEndian, &mse.ClientHandshakeSecret); err != nil { + return + } + if err = binary.Read(buf, binary.LittleEndian, &mse.ServerHandshakeSecret); err != nil { + return + } + if err = binary.Read(buf, binary.LittleEndian, &mse.ClientTrafficSecret); err != nil { + return + } + if err = binary.Read(buf, binary.LittleEndian, &mse.ServerTrafficSecret); err != nil { + return + } + if err = binary.Read(buf, binary.LittleEndian, &mse.ExporterMasterSecret); err != nil { + return + } + mse.payload = fmt.Sprintf("CLIENT_RANDOM %02x %02x", mse.ClientRandom, mse.MasterSecret) + return nil +} + +func (mse *MasterSecretGnutlsEvent) StringHex() string { + s := fmt.Sprintf("ClientRandom: %02x, MasterSecret: %02x", mse.ClientRandom[0:GnutlsRandomSize], mse.MasterSecret[0:GnutlsMasterSize]) + return s +} + +func (mse *MasterSecretGnutlsEvent) String() string { + s := fmt.Sprintf("ClientRandom: %02x, MasterSecret: %02x", mse.ClientRandom[0:GnutlsRandomSize], mse.MasterSecret[0:GnutlsMasterSize]) + return s +} + +func (mse *MasterSecretGnutlsEvent) Clone() IEventStruct { + event := new(MasterSecretGnutlsEvent) + event.eventType = EventTypeModuleData + return event +} + +func (mse *MasterSecretGnutlsEvent) EventType() EventType { + return mse.eventType +} + +func (mse *MasterSecretGnutlsEvent) GetUUID() string { + return fmt.Sprintf("%02X", mse.ClientRandom) +} + +func (mse *MasterSecretGnutlsEvent) Payload() []byte { + return []byte(mse.payload) +} + +func (mse *MasterSecretGnutlsEvent) PayloadLen() int { + return len(mse.payload) +} diff --git a/user/module/probe_gnutls.go b/user/module/probe_gnutls.go index 1befdc425..1a0f4bcb4 100644 --- a/user/module/probe_gnutls.go +++ b/user/module/probe_gnutls.go @@ -17,8 +17,13 @@ package module import ( "bytes" "context" - "errors" "fmt" + "io" + "os" + "path/filepath" + "sync" + "time" + "github.com/cilium/ebpf" manager "github.com/gojue/ebpfmanager" "github.com/gojue/ecapture/assets" @@ -26,18 +31,24 @@ import ( "github.com/gojue/ecapture/user/event" "github.com/rs/zerolog" "golang.org/x/sys/unix" - "io" - "math" - "os" - "path" ) type MGnutlsProbe struct { - Module + MTCProbe bpfManager *manager.Manager bpfManagerOptions manager.Options eventFuncMaps map[*ebpf.Map]event.IEventStruct eventMaps []*ebpf.Map + + pidConns map[uint32]map[uint32]string + pidLocker sync.Locker + + keyloggerFilename string + keylogger *os.File + masterKeys map[string]bool + eBPFProgramType TlsCaptureModelType + sslVersion string + sslBpfFile string } // 对象初始化 @@ -50,6 +61,51 @@ func (g *MGnutlsProbe) Init(ctx context.Context, logger *zerolog.Logger, conf co g.Module.SetChild(g) g.eventMaps = make([]*ebpf.Map, 0, 2) g.eventFuncMaps = make(map[*ebpf.Map]event.IEventStruct) + g.pidConns = make(map[uint32]map[uint32]string) + g.pidLocker = new(sync.Mutex) + g.masterKeys = make(map[string]bool) + model := g.conf.(*config.GnutlsConfig).Model + switch model { + case config.TlsCaptureModelKey, config.TlsCaptureModelKeylog: + g.eBPFProgramType = TlsCaptureModelTypeKeylog + g.keyloggerFilename = g.conf.(*config.GnutlsConfig).KeylogFile + file, err := os.OpenFile(g.keyloggerFilename, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0o600) + if err != nil { + return err + } + g.keylogger = file + case config.TlsCaptureModelPcap, config.TlsCaptureModelPcapng: + g.eBPFProgramType = TlsCaptureModelTypePcap + pcapFile := g.conf.(*config.GnutlsConfig).PcapFile + fileInfo, err := filepath.Abs(pcapFile) + if err != nil { + g.logger.Warn().Err(err).Str("pcapFile", pcapFile).Str("eBPFProgramType", g.eBPFProgramType.String()).Msg("pcapFile not found") + return err + } + g.tcPacketsChan = make(chan *TcPacket, 2048) + g.tcPackets = make([]*TcPacket, 0, 256) + g.pcapngFilename = fileInfo + case config.TlsCaptureModelText: + fallthrough + default: + g.eBPFProgramType = TlsCaptureModelTypeText + } + + var ts unix.Timespec + err = unix.ClockGettime(unix.CLOCK_MONOTONIC, &ts) + if err != nil { + return err + } + startTime := ts.Nano() + bootTime := time.Now().UnixNano() - startTime + + g.startTime = uint64(startTime) + g.bootTime = uint64(bootTime) + + g.tcPacketLocker = &sync.Mutex{} + g.masterKeyBuffer = bytes.NewBuffer([]byte{}) + + g.logger.Info().Str("model", g.eBPFProgramType.String()).Str("eBPFProgramType", g.eBPFProgramType.String()).Msg("GnuTlsProbe init") return nil } @@ -61,18 +117,36 @@ func (g *MGnutlsProbe) Start() error { } func (g *MGnutlsProbe) start() error { - + // get gnutls sslVersion and sslBpfFile + err := g.detectGnutls() + if err != nil { + g.logger.Error().Err(err).Msg("detectGnutls failed") + return fmt.Errorf("detectGnutls failed: %v", err) + } // fetch ebpf assets - var bpfFileName = g.geteBPFName("user/bytecode/gnutls_kern.o") - g.logger.Info().Str("bytecode filename", bpfFileName).Msg("BPF bytecode loaded") - byteBuf, err := assets.Asset(bpfFileName) + byteBuf, err := assets.Asset(g.sslBpfFile) if err != nil { g.logger.Error().Err(err).Strs("bytecode files", assets.AssetNames()).Msg("couldn't find bpf bytecode file") return fmt.Errorf("couldn't find asset %v", err) } // setup the managers - err = g.setupManagers() + switch g.eBPFProgramType { + case TlsCaptureModelTypeKeylog: + err = g.setupManagersKeylog() + case TlsCaptureModelTypePcap: + err = g.setupManagersPcap() + pcapFilter := g.conf.(*config.GnutlsConfig).PcapFilter + if g.eBPFProgramType == TlsCaptureModelTypePcap && pcapFilter != "" { + ebpfFuncs := []string{tcFuncNameIngress, tcFuncNameEgress} + g.bpfManager.InstructionPatchers = prepareInsnPatchers(g.bpfManager, + ebpfFuncs, pcapFilter) + } + case TlsCaptureModelTypeText: + fallthrough + default: + err = g.setupManagersText() + } if err != nil { return fmt.Errorf("tls(gnutls) module couldn't find binPath %v", err) } @@ -88,7 +162,16 @@ func (g *MGnutlsProbe) start() error { } // 加载map信息,map对应events decode表。 - err = g.initDecodeFun() + switch g.eBPFProgramType { + case TlsCaptureModelTypeKeylog: + err = g.initDecodeFunKeylog() + case TlsCaptureModelTypePcap: + err = g.initDecodeFunPcap() + case TlsCaptureModelTypeText: + fallthrough + default: + err = g.initDecodeFunText() + } if err != nil { return err } @@ -121,102 +204,28 @@ func (g *MGnutlsProbe) constantEditor() []manager.ConstantEditor { return editor } -func (g *MGnutlsProbe) setupManagers() error { - var binaryPath string - switch g.conf.(*config.GnutlsConfig).ElfType { - case config.ElfTypeSo: - binaryPath = g.conf.(*config.GnutlsConfig).Gnutls - default: - //如果没找到 "/lib/x86_64-linux-gnu/libgnutls.so.30" - binaryPath = path.Join(defaultSoPath, "libgnutls.so.30") - } - _, err := os.Stat(binaryPath) - if err != nil { - return err - } - - g.logger.Info().Str("binaryPath", binaryPath).Uint8("elfType", g.conf.(*config.GnutlsConfig).ElfType).Msg("gnutls binary path") - g.bpfManager = &manager.Manager{ - Probes: []*manager.Probe{ - { - Section: "uprobe/gnutls_record_send", - EbpfFuncName: "probe_entry_SSL_write", - AttachToFuncName: "gnutls_record_send", - BinaryPath: binaryPath, - }, - { - Section: "uretprobe/gnutls_record_send", - EbpfFuncName: "probe_ret_SSL_write", - AttachToFuncName: "gnutls_record_send", - BinaryPath: binaryPath, - }, - { - Section: "uprobe/gnutls_record_recv", - EbpfFuncName: "probe_entry_SSL_read", - AttachToFuncName: "gnutls_record_recv", - BinaryPath: binaryPath, - }, - { - Section: "uretprobe/gnutls_record_recv", - EbpfFuncName: "probe_ret_SSL_read", - AttachToFuncName: "gnutls_record_recv", - BinaryPath: binaryPath, - }, - }, - - Maps: []*manager.Map{ - { - Name: "gnutls_events", - }, - }, - } - - g.bpfManagerOptions = manager.Options{ - DefaultKProbeMaxActive: 512, - - VerifierOptions: ebpf.CollectionOptions{ - Programs: ebpf.ProgramOptions{ - LogSize: 2097152, - }, - }, - - RLimit: &unix.Rlimit{ - Cur: math.MaxUint64, - Max: math.MaxUint64, - }, - } - - if g.conf.EnableGlobalVar() { - // 填充 RewriteContants 对应map - g.bpfManagerOptions.ConstantEditors = g.constantEditor() - } - return nil -} - func (g *MGnutlsProbe) DecodeFun(em *ebpf.Map) (event.IEventStruct, bool) { fun, found := g.eventFuncMaps[em] return fun, found } -func (g *MGnutlsProbe) initDecodeFun() error { - //GnutlsEventsMap 与解码函数映射 - GnutlsEventsMap, found, err := g.bpfManager.GetMap("gnutls_events") - if err != nil { - return err - } - if !found { - return errors.New("cant found map:gnutls_events") - } - g.eventMaps = append(g.eventMaps, GnutlsEventsMap) - g.eventFuncMaps[GnutlsEventsMap] = &event.GnutlsDataEvent{} - - return nil -} - func (g *MGnutlsProbe) Events() []*ebpf.Map { return g.eventMaps } +func (g *MGnutlsProbe) Dispatcher(eventStruct event.IEventStruct) { + // detect eventStruct type + switch eventStruct.(type) { + case *event.MasterSecretGnutlsEvent: + g.saveMasterSecret(eventStruct.(*event.MasterSecretGnutlsEvent)) + case *event.TcSkbEvent: + err := g.dumpTcSkb(eventStruct.(*event.TcSkbEvent)) + if err != nil { + g.logger.Warn().Err(err).Msg("save packet error.") + } + } +} + func init() { RegisteFunc(NewGnutlsProbe) } diff --git a/user/module/probe_gnutls_keylog.go b/user/module/probe_gnutls_keylog.go new file mode 100644 index 000000000..064204955 --- /dev/null +++ b/user/module/probe_gnutls_keylog.go @@ -0,0 +1,181 @@ +// Author: yuweizzz . +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package module + +import ( + "bytes" + "errors" + "fmt" + "math" + + "github.com/cilium/ebpf" + manager "github.com/gojue/ebpfmanager" + "github.com/gojue/ecapture/user/config" + "github.com/gojue/ecapture/user/event" + "golang.org/x/sys/unix" +) + +// gnutls_mac_algorithm_t: https://github.com/gnutls/gnutls/blob/master/lib/includes/gnutls/gnutls.h.in#L365 +// gnutls_protocol_t: https://github.com/gnutls/gnutls/blob/master/lib/includes/gnutls/gnutls.h.in#L822 + +const ( + _ = iota + GNUTLS_SSL3, GNUTLS_DTLS1_0 = iota, iota + 200 + GNUTLS_TLS1_0, GNUTLS_DTLS1_2 = iota, iota + 200 + GNUTLS_TLS1_1 = iota + GNUTLS_TLS1_2 + GNUTLS_TLS1_3 + GNUTLS_MAC_SHA256 + GNUTLS_MAC_SHA384 +) + +var GnutlsVersionToString = map[int32]string{ + GNUTLS_SSL3: "GNUTLS_SSL3", + GNUTLS_TLS1_0: "GNUTLS_TLS1_0", + GNUTLS_TLS1_1: "GNUTLS_TLS1_1", + GNUTLS_TLS1_2: "GNUTLS_TLS1_2", + GNUTLS_TLS1_3: "GNUTLS_TLS1_3", + GNUTLS_DTLS1_0: "GNUTLS_DTLS1_0", + GNUTLS_DTLS1_2: "GNUTLS_DTLS1_2", +} + +func (g *MGnutlsProbe) setupManagersKeylog() error { + binaryPath := g.conf.(*config.GnutlsConfig).Gnutls + g.bpfManager = &manager.Manager{ + Probes: []*manager.Probe{ + { + Section: "uprobe/gnutls_handshake", + EbpfFuncName: "uprobe_gnutls_master_key", + AttachToFuncName: "gnutls_handshake", + BinaryPath: binaryPath, + }, + { + Section: "uretprobe/gnutls_handshake", + EbpfFuncName: "uretprobe_gnutls_master_key", + AttachToFuncName: "gnutls_handshake", + BinaryPath: binaryPath, + }, + }, + + Maps: []*manager.Map{ + { + Name: "mastersecret_gnutls_events", + }, + }, + } + + g.bpfManagerOptions = manager.Options{ + DefaultKProbeMaxActive: 512, + VerifierOptions: ebpf.CollectionOptions{ + Programs: ebpf.ProgramOptions{ + LogSize: 2097152, + }, + }, + + RLimit: &unix.Rlimit{ + Cur: math.MaxUint64, + Max: math.MaxUint64, + }, + } + + if g.conf.EnableGlobalVar() { + // 填充 RewriteContants 对应map + g.bpfManagerOptions.ConstantEditors = g.constantEditor() + } + return nil +} + +func (m *MGnutlsProbe) initDecodeFunKeylog() error { + MasterkeyEventsMap, found, err := m.bpfManager.GetMap("mastersecret_gnutls_events") + if err != nil { + return err + } + if !found { + return errors.New("cant found map: mastersecret_gnutls_events") + } + m.eventMaps = append(m.eventMaps, MasterkeyEventsMap) + + var masterkeyEvent event.IEventStruct + + masterkeyEvent = &event.MasterSecretGnutlsEvent{} + + m.eventFuncMaps[MasterkeyEventsMap] = masterkeyEvent + return nil +} + +func (g *MGnutlsProbe) saveMasterSecret(secretEvent *event.MasterSecretGnutlsEvent) { + clientRandomHex := fmt.Sprintf("%02x", secretEvent.ClientRandom[0:event.GnutlsRandomSize]) + k := fmt.Sprintf("%s-%s", "CLIENT_RANDOM", clientRandomHex) + + _, f := g.masterKeys[k] + if f { + // 已存在该随机数的masterSecret,不需要重复写入 + return + } + + g.masterKeys[k] = true + buf := bytes.NewBuffer(nil) + switch secretEvent.Version { + // tls1.3 + case GNUTLS_TLS1_3: + var length int + switch secretEvent.CipherId { + case GNUTLS_MAC_SHA384: + length = 48 + case GNUTLS_MAC_SHA256: + fallthrough + default: + // default MAC output length: 32 -- SHA256 + length = 32 + } + chSecret := secretEvent.ClientHandshakeSecret[0:length] + buf.WriteString(fmt.Sprintf("%s %s %02x\n", "CLIENT_HANDSHAKE_TRAFFIC_SECRET", clientRandomHex, chSecret)) + shSecret := secretEvent.ServerHandshakeSecret[0:length] + buf.WriteString(fmt.Sprintf("%s %s %02x\n", "SERVER_HANDSHAKE_TRAFFIC_SECRET", clientRandomHex, shSecret)) + emSecret := secretEvent.ExporterMasterSecret[0:length] + buf.WriteString(fmt.Sprintf("%s %s %02x\n", "EXPORTER_SECRET", clientRandomHex, emSecret)) + ctSecret := secretEvent.ClientTrafficSecret[0:length] + buf.WriteString(fmt.Sprintf("%s %s %02x\n", "CLIENT_TRAFFIC_SECRET_0", clientRandomHex, ctSecret)) + stSecret := secretEvent.ServerTrafficSecret[0:length] + buf.WriteString(fmt.Sprintf("%s %s %02x\n", "SERVER_TRAFFIC_SECRET_0", clientRandomHex, stSecret)) + // tls1.2 + case GNUTLS_TLS1_2: + fallthrough + // tls1.1, tls1.0, ssl3.0, dtls 1.0 and dtls 1.2 + default: + masterSecret := secretEvent.MasterSecret[0:event.GnutlsMasterSize] + buf.WriteString(fmt.Sprintf("%s %s %02x\n", "CLIENT_RANDOM", clientRandomHex, masterSecret)) + } + + var e error + switch g.eBPFProgramType { + case TlsCaptureModelTypeKeylog: + _, e = g.keylogger.WriteString(buf.String()) + if e != nil { + g.logger.Warn().Err(e).Str("ClientRandom", k).Msg("save masterSecrets to keylog error") + return + } + g.logger.Info().Str("TlsVersion", GnutlsVersionToString[secretEvent.Version]).Str("ClientRandom", clientRandomHex).Msg("CLIENT_RANDOM save success") + case TlsCaptureModelTypePcap: + e = g.savePcapngSslKeyLog(buf.Bytes()) + if e != nil { + g.logger.Warn().Err(e).Str("ClientRandom", k).Msg("save masterSecrets to pcapNG error") + return + } + g.logger.Info().Str("TlsVersion", GnutlsVersionToString[secretEvent.Version]).Str("ClientRandom", clientRandomHex).Str("eBPFProgramType", g.eBPFProgramType.String()).Msg("CLIENT_RANDOM save success") + default: + g.logger.Warn().Uint8("eBPFProgramType", uint8(g.eBPFProgramType)).Str("ClientRandom", clientRandomHex).Msg("unhandled default case with eBPF Program type") + } +} diff --git a/user/module/probe_gnutls_lib.go b/user/module/probe_gnutls_lib.go new file mode 100644 index 000000000..799472cf2 --- /dev/null +++ b/user/module/probe_gnutls_lib.go @@ -0,0 +1,158 @@ +// Author: yuweizzz . +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package module + +import ( + "debug/elf" + "fmt" + "os" + "path" + "regexp" + + "github.com/gojue/ecapture/user/config" +) + +const GnuTLSDefaultVersion = "3.6.12" +const GnuTLSVersionLen = 32 + +func readelf(binaryPath string) (string, error) { + f, err := os.OpenFile(binaryPath, os.O_RDONLY, os.ModePerm) + if err != nil { + return "", fmt.Errorf("Can not open %s, with error: %v", binaryPath, err) + } + defer f.Close() + r, err := elf.NewFile(f) + if err != nil { + return "", fmt.Errorf("Parse the ELF file %s failed, with error: %v", binaryPath, err) + } + defer r.Close() + + switch r.FileHeader.Machine { + case elf.EM_X86_64: + case elf.EM_AARCH64: + default: + return "", fmt.Errorf( + "Unsupported arch library, ELF Header Machine is: %s, must be one of EM_X86_64 and EM_AARCH64", + r.FileHeader.Machine.String()) + } + + s := r.Section(".rodata") + if s == nil { + // .rodata not found + return "", fmt.Errorf("Detect GnuTLS version failed, cant read .rodata section from %s", binaryPath) + } + + sectionOffset := int64(s.Offset) + sectionSize := s.Size + + _, err = f.Seek(0, 0) + if err != nil { + return "", err + } + + ret, err := f.Seek(sectionOffset, 0) + if ret != sectionOffset || err != nil { + return "", err + } + + rex, err := regexp.Compile(`Enabled GnuTLS ([0-9\.]+) logging...`) + if err != nil { + return "", err + } + + buf := make([]byte, 1024*1024) // 1Mb + totalReadCount := 0 + for totalReadCount < int(sectionSize) { + readCount, err := f.Read(buf) + if err != nil { + return "", err + } + + if readCount == 0 { + break + } + + match := rex.FindSubmatch(buf) + if len(match) == 2 { + return string(match[1]), nil + } + + // just like "probe_openssl_lib.go", + // "Enabled GnuTLS 3.x.xx logging..." is 32 chars. + totalReadCount += readCount - GnuTLSVersionLen + + _, err = f.Seek(sectionOffset+int64(totalReadCount), 0) + if err != nil { + return "", err + } + clear(buf) + } + return "", fmt.Errorf("Unknown error") +} + +func (m *MGnutlsProbe) detectGnutls() error { + var binaryPath string + switch m.conf.(*config.GnutlsConfig).ElfType { + case config.ElfTypeSo: + binaryPath = m.conf.(*config.GnutlsConfig).Gnutls + default: + // Default: "/lib/x86_64-linux-gnu/libgnutls.so.30" + binaryPath = path.Join(defaultSoPath, "libgnutls.so.30") + } + _, err := os.Stat(binaryPath) + if err != nil { + return err + } + + ConfigSslVersion := m.conf.(*config.GnutlsConfig).SslVersion + if len(ConfigSslVersion) > 0 { + m.sslVersion = ConfigSslVersion + m.logger.Info().Str("GnuTLS Version", m.sslVersion).Msg("GnuTLS version configured") + } else { + sslVersion, err := readelf(binaryPath) + if err != nil { + m.logger.Error().Err(err) + } + m.sslVersion = sslVersion + if len(m.sslVersion) == 0 { + m.logger.Warn().Str("GnuTLS Version", GnuTLSDefaultVersion).Msg("GnuTLS version not found, used default version") + m.sslVersion = GnuTLSDefaultVersion + } + m.logger.Info().Str("Version", m.sslVersion).Msg("GnuTLS version found") + } + + m.logger.Info().Str("binaryPath", binaryPath).Uint8("elfType", m.conf.(*config.GnutlsConfig).ElfType).Msg("GnuTLS binary path") + switch m.sslVersion { + case "3.8.7": + m.sslBpfFile = m.geteBPFName("user/bytecode/gnutls_3_8_7_kern.o") + case "3.8.6", "3.8.5", "3.8.4": + m.sslBpfFile = m.geteBPFName("user/bytecode/gnutls_3_8_4_kern.o") + case "3.8.3", "3.8.2", "3.8.1", "3.8.0", "3.7.11", "3.7.10", "3.7.9", "3.7.8", "3.7.7": + m.sslBpfFile = m.geteBPFName("user/bytecode/gnutls_3_7_7_kern.o") + case "3.7.6", "3.7.5", "3.7.4", "3.7.3": + m.sslBpfFile = m.geteBPFName("user/bytecode/gnutls_3_7_3_kern.o") + case "3.7.2", "3.7.1", "3.7.0": + m.sslBpfFile = m.geteBPFName("user/bytecode/gnutls_3_7_0_kern.o") + case "3.6.16", "3.6.15", "3.6.14": + m.sslBpfFile = m.geteBPFName("user/bytecode/gnutls_3_6_14_kern.o") + case "3.6.13", "3.6.12": + m.sslBpfFile = m.geteBPFName("user/bytecode/gnutls_3_6_12_kern.o") + default: + m.sslBpfFile = m.geteBPFName("user/bytecode/gnutls_3_6_12_kern.o") + m.logger.Warn().Msg("GnuTLS version not supported, used default bpf bytecode file") + } + m.logger.Info().Str("bytecode filename", m.sslBpfFile).Msg("BPF bytecode loaded") + return nil +} diff --git a/user/module/probe_gnutls_pcap.go b/user/module/probe_gnutls_pcap.go new file mode 100644 index 000000000..861714c95 --- /dev/null +++ b/user/module/probe_gnutls_pcap.go @@ -0,0 +1,178 @@ +// Author: yuweizzz . +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package module + +import ( + "errors" + "fmt" + "math" + "net" + + "github.com/cilium/ebpf" + manager "github.com/gojue/ebpfmanager" + "github.com/gojue/ecapture/user/config" + "github.com/gojue/ecapture/user/event" + "golang.org/x/sys/unix" +) + +func (m *MGnutlsProbe) setupManagersPcap() error { + binaryPath := m.conf.(*config.GnutlsConfig).Gnutls + ifname := m.conf.(*config.GnutlsConfig).Ifname + m.ifName = ifname + interf, err := net.InterfaceByName(m.ifName) + if err != nil { + return fmt.Errorf("InterfaceByName: %s , failed: %v", m.ifName, err) + } + + // loopback devices are special, some tc probes should be skipped + isNetIfaceLo := interf.Flags&net.FlagLoopback == net.FlagLoopback + skipLoopback := true // TODO: detect loopback devices via aquasecrity/tracee/pkg/ebpf/probes/probe.go line 322 + if isNetIfaceLo && skipLoopback { + return fmt.Errorf("%s\t%s is a loopback interface, skip it", m.Name(), m.ifName) + } + m.ifIdex = interf.Index + + pcapFilter := m.conf.(*config.GnutlsConfig).PcapFilter + m.logger.Info().Str("binrayPath", binaryPath).Str("IFname", m.ifName).Int("IFindex", m.ifIdex). + Str("PcapFilter", pcapFilter).Uint8("ElfType", m.conf.(*config.GnutlsConfig).ElfType).Msg("Hook type: Gnutls elf") + m.logger.Info().Msg("Hook masterKey function: gnutls_handshake") + + // create pcapng writer + netIfs, err := net.Interfaces() + if err != nil { + return err + } + + err = m.createPcapng(netIfs) + if err != nil { + return err + } + + // Serve pcapng writer to flush pcapng file + go func() { + m.ServePcap() + }() + + m.bpfManager = &manager.Manager{ + Probes: []*manager.Probe{ + // customize deleteed TC filter + // tc filter del dev eth0 ingress + // tc filter del dev eth0 egress + // loopback devices are special, some tc probes should be skipped + // TODO: detect loopback devices via aquasecrity/tracee/pkg/ebpf/probes/probe.go line 322 + // isNetIfaceLo := netIface.Flags&net.FlagLoopback == net.FlagLoopback + // if isNetIfaceLo && p.skipLoopback { + // return nil + // } + { + Section: "classifier/egress", + EbpfFuncName: tcFuncNameEgress, + Ifname: m.ifName, + NetworkDirection: manager.Egress, + }, + { + Section: "classifier/ingress", + EbpfFuncName: tcFuncNameIngress, + Ifname: m.ifName, + NetworkDirection: manager.Ingress, + }, + // -------------------------------------------------- + { + EbpfFuncName: "tcp_sendmsg", + Section: "kprobe/tcp_sendmsg", + AttachToFuncName: "tcp_sendmsg", + }, + { + EbpfFuncName: "udp_sendmsg", + Section: "kprobe/udp_sendmsg", + AttachToFuncName: "udp_sendmsg", + }, + // -------------------------------------------------- + { + Section: "uprobe/gnutls_handshake", + EbpfFuncName: "uprobe_gnutls_master_key", + AttachToFuncName: "gnutls_handshake", + BinaryPath: binaryPath, + UID: "uprobe_smk_gnutls_handshake", + }, + { + Section: "uretprobe/gnutls_handshake", + EbpfFuncName: "uretprobe_gnutls_master_key", + AttachToFuncName: "gnutls_handshake", + BinaryPath: binaryPath, + UID: "uretprobe_smk_gnutls_handshake", + }, + }, + + Maps: []*manager.Map{ + { + Name: "mastersecret_gnutls_events", + }, + { + Name: "skb_events", + }, + }, + } + + m.bpfManagerOptions = manager.Options{ + DefaultKProbeMaxActive: 512, + + VerifierOptions: ebpf.CollectionOptions{ + Programs: ebpf.ProgramOptions{ + LogSize: 2097152, + }, + }, + + RLimit: &unix.Rlimit{ + Cur: math.MaxUint64, + Max: math.MaxUint64, + }, + } + + if m.conf.EnableGlobalVar() { + // 填充 RewriteContants 对应map + m.bpfManagerOptions.ConstantEditors = m.constantEditor() + } + return nil +} + +func (m *MGnutlsProbe) initDecodeFunPcap() error { + // SkbEventsMap 与解码函数映射 + SkbEventsMap, found, err := m.bpfManager.GetMap("skb_events") + if err != nil { + return err + } + if !found { + return errors.New("cant found map:skb_events") + } + m.eventMaps = append(m.eventMaps, SkbEventsMap) + sslEvent := &event.TcSkbEvent{} + // sslEvent.SetModule(m) + m.eventFuncMaps[SkbEventsMap] = sslEvent + + MasterkeyEventsMap, found, err := m.bpfManager.GetMap("mastersecret_gnutls_events") + if err != nil { + return err + } + if !found { + return errors.New("cant found map: mastersecret_gnutls_events") + } + m.eventMaps = append(m.eventMaps, MasterkeyEventsMap) + + masterkeyEvent := &event.MasterSecretGnutlsEvent{} + // masterkeyEvent.SetModule(m) + m.eventFuncMaps[MasterkeyEventsMap] = masterkeyEvent + return nil +} diff --git a/user/module/probe_gnutls_text.go b/user/module/probe_gnutls_text.go new file mode 100644 index 000000000..51e5e74e9 --- /dev/null +++ b/user/module/probe_gnutls_text.go @@ -0,0 +1,99 @@ +// Author: yuweizzz . +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package module + +import ( + "errors" + "math" + + "github.com/cilium/ebpf" + manager "github.com/gojue/ebpfmanager" + "github.com/gojue/ecapture/user/config" + "github.com/gojue/ecapture/user/event" + "golang.org/x/sys/unix" +) + +func (m *MGnutlsProbe) setupManagersText() error { + binaryPath := m.conf.(*config.GnutlsConfig).Gnutls + m.bpfManager = &manager.Manager{ + Probes: []*manager.Probe{ + { + Section: "uprobe/gnutls_record_send", + EbpfFuncName: "probe_entry_SSL_write", + AttachToFuncName: "gnutls_record_send", + BinaryPath: binaryPath, + }, + { + Section: "uretprobe/gnutls_record_send", + EbpfFuncName: "probe_ret_SSL_write", + AttachToFuncName: "gnutls_record_send", + BinaryPath: binaryPath, + }, + { + Section: "uprobe/gnutls_record_recv", + EbpfFuncName: "probe_entry_SSL_read", + AttachToFuncName: "gnutls_record_recv", + BinaryPath: binaryPath, + }, + { + Section: "uretprobe/gnutls_record_recv", + EbpfFuncName: "probe_ret_SSL_read", + AttachToFuncName: "gnutls_record_recv", + BinaryPath: binaryPath, + }, + }, + + Maps: []*manager.Map{ + { + Name: "gnutls_events", + }, + }, + } + + m.bpfManagerOptions = manager.Options{ + DefaultKProbeMaxActive: 512, + + VerifierOptions: ebpf.CollectionOptions{ + Programs: ebpf.ProgramOptions{ + LogSize: 2097152, + }, + }, + + RLimit: &unix.Rlimit{ + Cur: math.MaxUint64, + Max: math.MaxUint64, + }, + } + + if m.conf.EnableGlobalVar() { + // 填充 RewriteContants 对应map + m.bpfManagerOptions.ConstantEditors = m.constantEditor() + } + return nil +} + +func (m *MGnutlsProbe) initDecodeFunText() error { + //GnutlsEventsMap 与解码函数映射 + GnutlsEventsMap, found, err := m.bpfManager.GetMap("gnutls_events") + if err != nil { + return err + } + if !found { + return errors.New("cant found map: gnutls_events") + } + m.eventMaps = append(m.eventMaps, GnutlsEventsMap) + m.eventFuncMaps[GnutlsEventsMap] = &event.GnutlsDataEvent{} + return nil +} diff --git a/utils/gnutls_offset.c b/utils/gnutls_offset.c new file mode 100644 index 000000000..ff767ae2d --- /dev/null +++ b/utils/gnutls_offset.c @@ -0,0 +1,59 @@ +// bootstrap +// configure +// clang -I gnulib/lib/ -I lib/ -I . gnutls_offset.c -o offset + +#include +#include +#include +#include + +#define SSL_STRUCT_OFFSETS \ + X(gnutls_session_int, security_parameters) \ + X(gnutls_session_int, security_parameters.prf) \ + X(mac_entry_st, id) \ + X(gnutls_session_int, security_parameters.client_random) \ + X(gnutls_session_int, security_parameters.master_secret) \ + X(gnutls_session_int, key.proto.tls13.hs_ckey) \ + X(gnutls_session_int, key.proto.tls13.hs_skey) \ + X(gnutls_session_int, key.proto.tls13.ap_ckey) \ + X(gnutls_session_int, key.proto.tls13.ap_skey) \ + X(gnutls_session_int, key.proto.tls13.ap_expkey) + +#define SSL_ANY_STRUCT_OFFSETS \ + Y(security_parameters_st, pversion) \ + Y(version_entry_st, id) + +void toUpper(char *s) { + int i = 0; + while (s[i] != '\0') { + if (s[i] == '.') { + putchar('_'); + } else { + putchar(toupper(s[i])); + } + i++; + } +} + +void format(char *struct_name, char *field_name, size_t offset) { + printf("// %s->%s\n", struct_name, field_name); + printf("#define "); + toUpper(struct_name); + printf("_"); + toUpper(field_name); + printf(" 0x%lx\n\n", offset); +} + + +int main() { + +#define X(struct_name, field_name) format(#struct_name, #field_name, offsetof(struct struct_name, field_name)); + SSL_STRUCT_OFFSETS +#undef X + +#define Y(struct_name, field_name) format(#struct_name, #field_name, offsetof(struct_name, field_name)); + SSL_ANY_STRUCT_OFFSETS +#undef Y + + return 0; +} diff --git a/utils/gnutls_offset.sh b/utils/gnutls_offset.sh new file mode 100755 index 000000000..f4966054e --- /dev/null +++ b/utils/gnutls_offset.sh @@ -0,0 +1,79 @@ +#!/usr/bin/env bash +set -e + +PROJECT_ROOT_DIR=$(pwd) +GNUTLS_DIR="${PROJECT_ROOT_DIR}/deps/gnutls" +OUTPUT_DIR="${PROJECT_ROOT_DIR}/kern" + +if [[ ! -f "go.mod" ]]; then + echo "Run the script from the project root directory" + exit 1 +fi + +echo "check file exists: ${GNUTLS_DIR}/.git" +# skip cloning if the header file of the max supported version is already generated +if [[ ! -f "${GNUTLS_DIR}/.git" ]]; then + echo "check directory exists: ${GNUTLS_DIR}" + # skip cloning if the gnutls directory already exists + if [[ ! -d "${GNUTLS_DIR}" ]]; then + echo "git clone gnutls to ${GNUTLS_DIR}" + git clone https://github.com/gnutls/gnutls.git ${GNUTLS_DIR} + fi +fi + + +function run() { + git fetch --tags + cp -f ${PROJECT_ROOT_DIR}/utils/gnutls_offset.c ${GNUTLS_DIR}/offset.c + main_version="3.8" + + for ver in $(seq 7 8); do + tag="${main_version}.${ver}" + underline_tag=$(echo $tag | tr "." "_") + header_file="${OUTPUT_DIR}/gnutls_${underline_tag}_kern.c" + header_define="GNUTLS_${underline_tag}_KERN_H" + + if [[ -f ${header_file} ]]; then + echo "Skip ${header_file}" + continue + fi + echo "git checkout ${tag}" + git checkout ${tag} + echo "Generating ${header_file}" + + # init + ./bootstrap --skip-po --force --no-bootstrap-sync + ./configure --without-p11-kit --without-brotli --without-zstd --without-zlib --without-tpm + clang -I gnulib/lib/ -I lib/includes -I . offset.c -o offset + + echo -e "#ifndef ECAPTURE_${header_define}" >${header_file} + echo -e "#define ECAPTURE_${header_define}\n" >>${header_file} + ./offset >>${header_file} + echo -e "\n#include \"gnutls.h\"" >>${header_file} + echo -e "#include \"gnutls_masterkey.h\"" >>${header_file} + echo -e "\n#endif" >>${header_file} + + # clean up + make clean + done + + rm offset.c +} + +# install deps +sudo apt install -y \ + libtool \ + gettext \ + gperf \ + autopoint \ + gtk-doc-tools \ + nettle-dev \ + libev-dev \ + libtasn1-6-dev \ + libunistring-dev \ + libunbound-dev + +pushd ${GNUTLS_DIR} +(run) +[[ "$?" != 0 ]] && popd +popd diff --git a/variables.mk b/variables.mk index 2fa62f383..5fd956a10 100644 --- a/variables.mk +++ b/variables.mk @@ -204,7 +204,13 @@ TARGETS += kern/gotls ifeq ($(ANDROID),0) TARGETS += kern/bash - TARGETS += kern/gnutls + TARGETS += kern/gnutls_3_6_12 + TARGETS += kern/gnutls_3_6_14 + TARGETS += kern/gnutls_3_7_0 + TARGETS += kern/gnutls_3_7_3 + TARGETS += kern/gnutls_3_7_7 + TARGETS += kern/gnutls_3_8_4 + TARGETS += kern/gnutls_3_8_7 TARGETS += kern/nspr TARGETS += kern/mysqld TARGETS += kern/postgres