codesigned/
lib.rs

1#[macro_use]
2extern crate log;
3
4mod winapi;
5
6extern crate widestring;
7
8use std::ptr::{null_mut, null, read};
9use widestring::WideCString;
10use winapi::*;
11
12#[derive(Clone,Default,Debug)]
13pub struct CodeSigned {
14    pub path: String,
15    pub signed: Option<bool>,
16    pub catalog: bool,
17    pub issuer_name: String,
18    pub subject_name: String,
19    pub timestamp_issuer_name: String,
20    pub timestamp_subject_name: String,
21    pub serial_number: String,
22}
23
24impl CodeSigned {
25    pub fn file(&mut self, path: &str) {
26        self.path = path.to_owned();
27        if let Ok(path) = WideCString::from_str(path) {
28            let mut file_info = WinTrustFileInfo::from_path(&path);
29            let mut win_trust_data = WinTrustData::default();
30            win_trust_data.data = &mut file_info;
31
32            let action = Guid::wintrust_action_generic_verify_v2();
33            match unsafe {
34                WinVerifyTrust(null(), &action, &win_trust_data)
35            } {
36                0 => self.embedded(&path),
37                _ => self.catalog(&path)
38            }
39
40            win_trust_data.state_action = WTD_STATEACTION_CLOSE;
41            unsafe { WinVerifyTrust(null(), &action, &win_trust_data); }
42        }
43    }
44
45    fn embedded(&mut self, path: &WideCString) {
46        let mut encoding: u32 = 0;
47        let mut content_type: u32 = 0;
48        let mut format_type: u32 = 0;
49        let mut h_store: *mut u8 = null_mut();
50        let mut h_msg: *mut u8 = null_mut();
51
52
53        match unsafe {
54            CryptQueryObject(
55                CERT_QUERY_OBJECT_FILE,
56                path.as_ptr() as *const _,
57                CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
58                CERT_QUERY_FORMAT_FLAG_BINARY,
59                0,
60                &mut encoding,
61                &mut content_type,
62                &mut format_type,
63                &mut h_store,
64                &mut h_msg,
65                null_mut()
66            )
67        } {
68            0 => { /* error */ },
69            _ => {
70                let mut data_len: u32 = 0;
71                unsafe {
72                    CryptMsgGetParam(
73                        h_msg,
74                        CMSG_SIGNER_INFO_PARAM,
75                        0,
76                        null_mut(),
77                        &mut data_len
78                    );
79                }
80
81                let mut data: Vec<u8> = vec![0; data_len as usize];
82                match unsafe {
83                    CryptMsgGetParam(
84                        h_msg,
85                        CMSG_SIGNER_INFO_PARAM,
86                        0,
87                        data.as_mut_ptr(),
88                        &mut data_len
89                    )
90                } {
91                    0 => { /* error */ },
92                    _ => {
93                        let msg: MsgSignerInfo = unsafe {
94                            read(data.as_mut_ptr() as *const _)
95                        };
96
97                        let mut cert_info = CertInfo::default();
98                        cert_info.serial_number.from(&msg.serial_number);
99                        cert_info.issuer.from(&msg.issuer);
100
101                        let context = CertStoreContext::new(unsafe {
102                            CertFindCertificateInStore(
103                                h_store,
104                                ENCODING,
105                                0,
106                                CERT_FIND_SUBJECT_CERT,
107                                &cert_info,
108                                null()
109                            )
110                        });
111
112                        let needed: u32 = unsafe {
113                            CertGetNameStringA(
114                                context.context(),
115                                CERT_NAME_SIMPLE_DISPLAY_TYPE,
116                                0,
117                                null(),
118                                null(),
119                                0
120                            )
121                        };
122                        let mut subject_name_data: Vec<u8> = vec![0; needed as usize];
123                        unsafe {
124                            CertGetNameStringA(
125                                context.context(),
126                                CERT_NAME_SIMPLE_DISPLAY_TYPE,
127                                0,
128                                null(),
129                                subject_name_data.as_mut_ptr(),
130                                needed as u32
131                            );
132                        }
133
134                        self.serial_number = msg.serial_number.to_string();
135                        self.issuer_name = msg.issuer.to_string();
136                        self.subject_name = String::from_utf8((&subject_name_data[0..needed as usize -1]).to_vec()).unwrap_or("(unknown)".to_owned());
137                        self.signed = Some(true);
138                        unsafe {
139                            CryptMsgClose(h_msg);
140                            CertCloseStore(h_store, 2);
141                        }
142                    }
143                }
144            }
145        }
146    }
147
148    fn catalog(&mut self, path: &WideCString) {
149        let f = FileHandle::new().with_file(&path);
150        if let Some(handle) = f.handle() {
151            let mut hash_length: u32 = 0;
152            match unsafe {
153                CryptCATAdminCalcHashFromFileHandle(
154                    handle,
155                    &mut hash_length,
156                    null_mut(),
157                    0
158                )
159            } {
160                0 => warn!("Could not obtain file hash for {}", path.to_string_lossy()),
161                _ => {
162                    self.signed = Some(false);
163                    let mut hash_data: Vec<u8> = vec![0; hash_length as usize];
164                    unsafe {
165                        CryptCATAdminCalcHashFromFileHandle(
166                            handle,
167                            &mut hash_length,
168                            hash_data.as_mut_ptr(),
169                            0
170                        );
171                    }
172
173                    let driver_action = Guid::driver_action_verify();
174                    let mut admin_context: *mut u8 = null_mut();
175                    if unsafe { CryptCATAdminAcquireContext(&mut admin_context, &driver_action, 0) } == 0 {
176                        /* error? */
177                        return;
178                    }
179
180                    let mut cat = unsafe { CryptCATAdminEnumCatalogFromHash(admin_context, hash_data.as_ptr(), hash_length, 0, null_mut()) };
181                    loop {
182                        let mut cat_info = CatalogInfo::default();
183                        if 0 == unsafe { CryptCATCatalogInfoFromContext(cat, &mut cat_info, 0) } {
184                            /* out of catalogs */
185                            break;
186                        }
187                        let cat_path = unsafe { WideCString::from_ptr_str(&cat_info.catalog_file as *const u16) };
188                        self.catalog = true;
189                        self.embedded(&cat_path);
190                        cat = unsafe { CryptCATAdminEnumCatalogFromHash(admin_context, hash_data.as_ptr(), hash_length, 0, &mut cat) };
191                        if cat == null_mut() { break; }
192                    }
193
194                    unsafe {
195                        CryptCATAdminReleaseCatalogContext(admin_context, cat, 0);
196                        CryptCATAdminReleaseContext(admin_context, 0);
197                    }
198                }
199            }
200        }
201    }
202}