Drop privileges very early on.
[metze/wireshark/wip.git] / capture_win_ifnames.c
1 /* capture_win_ifnames.c
2 * Routines supporting the use of Windows friendly interface names within Wireshark
3 * Copyright 2011-2012, Mike Garratt <wireshark@evn.co.nz>
4 *
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 */
23
24 #include "config.h"
25
26 #ifdef _WIN32
27
28 #include <winsock2.h>
29 #include <windows.h>
30 #include <iphlpapi.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33
34 #include <wtap.h>
35 #include <libpcap.h>
36 #include <glib.h>
37
38 #include <ntddndis.h>
39
40 #ifndef NDIS_IF_MAX_STRING_SIZE
41 #define NDIS_IF_MAX_STRING_SIZE IF_MAX_STRING_SIZE   /* =256 in <ifdef.h> */
42 #endif
43
44 #ifndef NETIO_STATUS
45 #define NETIO_STATUS DWORD
46 #endif
47
48 #include "log.h"
49
50 #include <capchild/capture_ifinfo.h>
51 #include "capture_win_ifnames.h"
52 #include "wsutil/file_util.h"
53
54 static int gethexdigit(const char *p)
55 {
56     if(*p >= '0' && *p <= '9'){
57         return *p - '0';
58     }else if(*p >= 'A' && *p <= 'F'){
59         return *p - 'A' + 0xA;
60     }else if(*p >= 'a' && *p <= 'f'){
61         return *p - 'a' + 0xa;
62     }else{
63         return -1; /* Not a hex digit */
64     }
65 }
66
67 static gboolean get8hexdigits(const char *p, DWORD *d)
68 {
69     int digit;
70     DWORD val;
71     int i;
72
73     val = 0;
74     for(i = 0; i < 8; i++){
75         digit = gethexdigit(p++);
76         if(digit == -1){
77             return FALSE; /* Not a hex digit */
78         }
79         val = (val << 4) | digit;
80     }
81     *d = val;
82     return TRUE;
83 }
84
85 static gboolean get4hexdigits(const char *p, WORD *w)
86 {
87     int digit;
88     WORD val;
89     int i;
90
91     val = 0;
92     for(i = 0; i < 4; i++){
93         digit = gethexdigit(p++);
94         if(digit == -1){
95             return FALSE; /* Not a hex digit */
96         }
97         val = (val << 4) | digit;
98     }
99     *w = val;
100     return TRUE;
101 }
102
103 /*
104  * If a string is a GUID in {}, fill in a GUID structure with the GUID
105  * value and return TRUE; otherwise, if the string is not a valid GUID
106  * in {}, return FALSE.
107  */
108 gboolean
109 parse_as_guid(const char *guid_text, GUID *guid)
110 {
111     int i;
112     int digit1, digit2;
113
114     if(*guid_text != '{'){
115         return FALSE; /* Nope, not enclosed in {} */
116     }
117     guid_text++;
118     /* There must be 8 hex digits; if so, they go into guid->Data1 */
119     if(!get8hexdigits(guid_text, &guid->Data1)){
120         return FALSE; /* nope, not 8 hex digits */
121     }
122     guid_text += 8;
123     /* Now there must be a hyphen */
124     if(*guid_text != '-'){
125         return FALSE; /* Nope */
126     }
127     guid_text++;
128     /* There must be 4 hex digits; if so, they go into guid->Data2 */
129     if(!get4hexdigits(guid_text, &guid->Data2)){
130         return FALSE; /* nope, not 4 hex digits */
131     }
132     guid_text += 4;
133     /* Now there must be a hyphen */
134     if(*guid_text != '-'){
135         return FALSE; /* Nope */
136     }
137     guid_text++;
138     /* There must be 4 hex digits; if so, they go into guid->Data3 */
139     if(!get4hexdigits(guid_text, &guid->Data3)){
140         return FALSE; /* nope, not 4 hex digits */
141     }
142     guid_text += 4;
143     /* Now there must be a hyphen */
144     if(*guid_text != '-'){
145         return FALSE; /* Nope */
146     }
147     guid_text++;
148     /*
149      * There must be 4 hex digits; if so, they go into the first 2 bytes
150      * of guid->Data4.
151      */
152     for(i = 0; i < 2; i++){
153         digit1 = gethexdigit(guid_text);
154         if(digit1 == -1){
155             return FALSE; /* Not a hex digit */
156         }
157         guid_text++;
158         digit2 = gethexdigit(guid_text);
159         if(digit2 == -1){
160             return FALSE; /* Not a hex digit */
161         }
162         guid_text++;
163         guid->Data4[i] = (digit1 << 4)|(digit2);
164     }
165     /* Now there must be a hyphen */
166     if(*guid_text != '-'){
167         return FALSE; /* Nope */
168     }
169     guid_text++;
170     /*
171      * There must be 12 hex digits; if so,t hey go into the next 6 bytes
172      * of guid->Data4.
173      */
174     for(i = 0; i < 6; i++){
175         digit1 = gethexdigit(guid_text);
176         if(digit1 == -1){
177             return FALSE; /* Not a hex digit */
178         }
179         guid_text++;
180         digit2 = gethexdigit(guid_text);
181         if(digit2 == -1){
182             return FALSE; /* Not a hex digit */
183         }
184         guid_text++;
185         guid->Data4[i+2] = (digit1 << 4)|(digit2);
186     }
187     /* Now there must be a closing } */
188     if(*guid_text != '}'){
189         return FALSE; /* Nope */
190     }
191     guid_text++;
192     /* And that must be the end of the string */
193     if(*guid_text != '\0'){
194         return FALSE; /* Nope */
195     }
196     return TRUE;
197 }
198
199 /**********************************************************************************/
200 gboolean IsWindowsVistaOrLater()
201 {
202 #if (_MSC_VER >= 1800)
203     /*
204      * On VS2103, GetVersionEx is deprecated. Microsoft recommend to
205      * use VerifyVersionInfo instead
206      */
207     OSVERSIONINFOEX osvi;
208     DWORDLONG dwlConditionMask = 0;
209     int op = VER_GREATER_EQUAL;
210
211     SecureZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
212     osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
213     osvi.dwMajorVersion = 6;
214     VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, op);
215     return VerifyVersionInfo(&osvi, VER_MAJORVERSION, dwlConditionMask);
216 #else
217     OSVERSIONINFO osvi;
218
219     SecureZeroMemory(&osvi, sizeof(OSVERSIONINFO));
220     osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
221
222     if(GetVersionEx(&osvi)){
223         return osvi.dwMajorVersion >= 6;
224     }
225     return FALSE;
226 #endif
227 }
228
229 /**********************************************************************************/
230 /* Get the friendly name for the given GUID */
231 char *
232 get_interface_friendly_name_from_device_guid(__in GUID *guid)
233 {
234     HMODULE hIPHlpApi;
235     HRESULT status;
236     WCHAR wName[NDIS_IF_MAX_STRING_SIZE + 1];
237     HRESULT hr;
238     gboolean fallbackToUnpublishedApi=TRUE;
239     gboolean haveInterfaceFriendlyName=FALSE;
240     int size;
241     char *name;
242
243     /* Load the ip helper api DLL */
244     hIPHlpApi = LoadLibrary(TEXT("iphlpapi.dll"));
245     if (hIPHlpApi == NULL) {
246         /* Load failed - DLL should always be available in XP+*/
247         return NULL;
248     }
249
250     /* Need to convert an Interface GUID to the interface friendly name (e.g. "Local Area Connection")
251     * The functions required to do this all reside within iphlpapi.dll
252     * - The preferred approach is to use published API functions (Available since Windows Vista)
253     * - We do however fallback to trying undocumented API if the published API is not available (Windows XP/2k3 scenario)
254     */
255
256     if(IsWindowsVistaOrLater()){
257         /* Published API function prototypes (for Windows Vista/Windows Server 2008+) */
258         typedef NETIO_STATUS (WINAPI *ProcAddr_CIG2L) (__in CONST GUID *InterfaceGuid, __out PNET_LUID InterfaceLuid);
259         typedef NETIO_STATUS (WINAPI *ProcAddr_CIL2A) ( __in CONST NET_LUID *InterfaceLuid,__out_ecount(Length) PWSTR InterfaceAlias, __in SIZE_T Length);
260
261         /* Attempt to do the conversion using Published API functions */
262         ProcAddr_CIG2L proc_ConvertInterfaceGuidToLuid=(ProcAddr_CIG2L) GetProcAddress(hIPHlpApi, "ConvertInterfaceGuidToLuid");
263         if(proc_ConvertInterfaceGuidToLuid!=NULL){
264             ProcAddr_CIL2A Proc_ConvertInterfaceLuidToAlias=(ProcAddr_CIL2A) GetProcAddress(hIPHlpApi, "ConvertInterfaceLuidToAlias");
265             if(Proc_ConvertInterfaceLuidToAlias!=NULL){
266                 /* we have our functions ready to go, attempt to convert interface guid->luid->friendlyname */
267                 NET_LUID InterfaceLuid;
268                 hr = proc_ConvertInterfaceGuidToLuid(guid, &InterfaceLuid);
269                 if(hr==NO_ERROR){
270                     /* guid->luid success */
271                     hr = Proc_ConvertInterfaceLuidToAlias(&InterfaceLuid, wName, NDIS_IF_MAX_STRING_SIZE+1);
272
273                     if(hr==NO_ERROR){
274                         /* luid->friendly name success */
275                         haveInterfaceFriendlyName=TRUE; /* success */
276                     }else{
277                         /* luid->friendly name failed */
278                         fallbackToUnpublishedApi=FALSE;
279                     }
280                 }else{
281                     fallbackToUnpublishedApi=FALSE;
282                 }
283
284             }
285         }
286     }
287
288
289     if(fallbackToUnpublishedApi && !haveInterfaceFriendlyName){
290         /* Didn't manage to get the friendly name using published api functions
291         * (most likely cause wireshark is running on Windows XP/Server 2003)
292         * Retry using nhGetInterfaceNameFromGuid (an older unpublished API function) */
293         typedef HRESULT (WINAPI *ProcAddr_nhGINFG) (__in GUID *InterfaceGuid, __out PCWSTR InterfaceAlias, __inout DWORD *LengthAddress, wchar_t *a4, wchar_t *a5);
294
295         ProcAddr_nhGINFG Proc_nhGetInterfaceNameFromGuid = NULL;
296         Proc_nhGetInterfaceNameFromGuid = (ProcAddr_nhGINFG) GetProcAddress(hIPHlpApi, "NhGetInterfaceNameFromGuid");
297         if (Proc_nhGetInterfaceNameFromGuid!= NULL) {
298             wchar_t *p4=NULL, *p5=NULL;
299             DWORD NameSize;
300
301             /* testing of nhGetInterfaceNameFromGuid indicates the unpublished API function expects the 3rd parameter
302             * to be the available space in bytes (as compared to wchar's) available in the second parameter buffer
303             * to receive the friendly name (in unicode format) including the space for the nul termination.*/
304             NameSize = sizeof(wName);
305
306             /* do the guid->friendlyname lookup */
307             status = Proc_nhGetInterfaceNameFromGuid(guid, wName, &NameSize, p4, p5);
308
309             if(status==0){
310                 haveInterfaceFriendlyName=TRUE; /* success */
311             }
312         }
313     }
314
315     /* we have finished with iphlpapi.dll - release it */
316     FreeLibrary(hIPHlpApi);
317
318     if(!haveInterfaceFriendlyName){
319         /* failed to get the friendly name, nothing further to do */
320         return NULL;
321     }
322
323     /* Get the required buffer size, and then convert the string
324     * from UTF-16 to UTF-8. */
325     size=WideCharToMultiByte(CP_UTF8, 0, wName, -1, NULL, 0, NULL, NULL);
326     name=(char *) g_malloc(size);
327     if (name == NULL){
328         return NULL;
329     }
330     size=WideCharToMultiByte(CP_UTF8, 0, wName, -1, name, size, NULL, NULL);
331     if(size==0){
332         /* bytes written == 0, indicating some form of error*/
333         g_free(name);
334         return NULL;
335     }
336     return name;
337 }
338
339 /*
340  * Given an interface name, try to extract the GUID from it and parse it.
341  * If that fails, return NULL; if that succeeds, attempt to get the
342  * friendly name for the interface in question.  If that fails, return
343  * NULL, otherwise return the friendly name, allocated with g_malloc()
344  * (so that it must be freed with g_free()).
345  */
346 char *
347 get_windows_interface_friendly_name(const char *interface_devicename)
348 {
349     const char* guid_text;
350     GUID guid;
351
352     /* Extract the guid text from the interface device name */
353     if(strncmp("\\Device\\NPF_", interface_devicename, 12)==0){
354         guid_text=interface_devicename+12; /* skip over the '\Device\NPF_' prefix, assume the rest is the guid text */
355     }else{
356         guid_text=interface_devicename;
357     }
358
359     if (!parse_as_guid(guid_text, &guid)){
360         return NULL; /* not a GUID, so no friendly name */
361     }
362
363     /* guid okay, get the interface friendly name associated with the guid */
364     return get_interface_friendly_name_from_device_guid(&guid);
365 }
366
367 /**************************************************************************************/
368 #endif
369