1 | /** $Id: VBoxUSBHelper.cpp 31898 2010-08-24 09:28:43Z vboxsync $ */
|
---|
2 | /** @file
|
---|
3 | * VBoxUSBHelper, setuid binary wrapper for dynamic driver alias updating.
|
---|
4 | */
|
---|
5 |
|
---|
6 | /*
|
---|
7 | * Copyright (C) 2008-2009 Oracle Corporation
|
---|
8 | *
|
---|
9 | * This file is part of VirtualBox Open Source Edition (OSE), as
|
---|
10 | * available from http://www.virtualbox.org. This file is free software;
|
---|
11 | * you can redistribute it and/or modify it under the terms of the GNU
|
---|
12 | * General Public License (GPL) as published by the Free Software
|
---|
13 | * Foundation, in version 2 as it comes in the "COPYING" file of the
|
---|
14 | * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
|
---|
15 | * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
|
---|
16 | */
|
---|
17 |
|
---|
18 | /*
|
---|
19 | * I think it is currently a security risk using VBoxUSBHelper. By that I mean
|
---|
20 | * here we have an application that can add/remove driver aliases in a generic
|
---|
21 | * fashion without requiring root privileges (well setuid).
|
---|
22 | *
|
---|
23 | * Later maybe we can change this to hardcode aliases to prefix "usb" and then
|
---|
24 | * hardcode the driver as "ugen".
|
---|
25 | */
|
---|
26 |
|
---|
27 | /*******************************************************************************
|
---|
28 | * Header Files *
|
---|
29 | *******************************************************************************/
|
---|
30 | #include <stdio.h>
|
---|
31 | #include <unistd.h>
|
---|
32 | #include <stdlib.h>
|
---|
33 | #include <string.h>
|
---|
34 | #include <limits.h>
|
---|
35 | #include <libdevice.h>
|
---|
36 | #include <errno.h>
|
---|
37 | #include <syslog.h>
|
---|
38 |
|
---|
39 | typedef enum AliasOp
|
---|
40 | {
|
---|
41 | ADD_ALIAS = 12,
|
---|
42 | DEL_ALIAS
|
---|
43 | } AliasOp;
|
---|
44 |
|
---|
45 | /**
|
---|
46 | * Print usage to stdout.
|
---|
47 | */
|
---|
48 | static void Usage(char *pszName)
|
---|
49 | {
|
---|
50 | /* @todo Eventually remove the usage syntax display & only display the copyright and warning. */
|
---|
51 | fprintf(stderr, "Oracle VM VirtualBox USB Helper\nCopyright (C) Oracle Corporation\n\n");
|
---|
52 | #if 0
|
---|
53 | fprintf(stderr, "Usage:\n");
|
---|
54 | fprintf(stderr, "Add: %s add <alias> <drivername>\n", pszName);
|
---|
55 | fprintf(stderr, "Delete: %s del <alias> <drivername>\n", pszName);
|
---|
56 | fprintf(stderr, "Config: %s cfg <device>\n\n", pszName);
|
---|
57 | fprintf(stderr, "Warning!! This program is internally used by VirtualBox and not meant to be used directly.\n");
|
---|
58 | #endif
|
---|
59 | exit(-1);
|
---|
60 | }
|
---|
61 |
|
---|
62 | /**
|
---|
63 | * Return code legend:
|
---|
64 | * 0 - success.
|
---|
65 | * -1 - no root permission
|
---|
66 | * -2 - insufficient arguments
|
---|
67 | * -3 - abormal termination of udpate_drv (not likely)
|
---|
68 | * -4 - update_drv failed (mostly trying to add already existing alias)
|
---|
69 | */
|
---|
70 | static int UpdateDrv(AliasOp Op, char *pszAlias, char *pszDrv)
|
---|
71 | {
|
---|
72 | char szAlias[256];
|
---|
73 | sprintf(szAlias, "\"%s\"", pszAlias);
|
---|
74 |
|
---|
75 | /* Store current user ID and raise privilege. */
|
---|
76 | uid_t userID = getuid();
|
---|
77 | setuid(0);
|
---|
78 |
|
---|
79 | /* For adding alias add the driver once to make sure we can update alias, no error checking needed. */
|
---|
80 | if (Op == ADD_ALIAS)
|
---|
81 | {
|
---|
82 | char szAddDrvCmd[512];
|
---|
83 | sprintf(szAddDrvCmd, "/usr/sbin/add_drv %s\n", pszDrv);
|
---|
84 | system(szAddDrvCmd);
|
---|
85 | }
|
---|
86 |
|
---|
87 | /* Update the alias. */
|
---|
88 | char *pszArgs[6];
|
---|
89 | int rc = 0;
|
---|
90 | pszArgs[0] = "/usr/sbin/update_drv";
|
---|
91 | pszArgs[1] = (char *)(Op == ADD_ALIAS ? "-a" : "-d");
|
---|
92 | pszArgs[2] = "-i";
|
---|
93 | pszArgs[3] = pszAlias;
|
---|
94 | pszArgs[4] = pszDrv;
|
---|
95 | pszArgs[5] = NULL;
|
---|
96 | pid_t processID = fork();
|
---|
97 | if (processID < 0)
|
---|
98 | {
|
---|
99 | /* Bad. fork() failed! */
|
---|
100 | rc = -2;
|
---|
101 | }
|
---|
102 | if (processID == 0)
|
---|
103 | {
|
---|
104 | /* Child process. */
|
---|
105 | syslog(LOG_ERR, "VBoxUSBHelper: %s %s %s %s %s\n", pszArgs[0], pszArgs[1], pszArgs[2], pszArgs[3], pszArgs[4]);
|
---|
106 | execv(pszArgs[0], pszArgs);
|
---|
107 | _exit(1);
|
---|
108 | }
|
---|
109 |
|
---|
110 | /* Parent process. */
|
---|
111 | int result;
|
---|
112 | while (waitpid(processID, &result, 0) < 0)
|
---|
113 | ;
|
---|
114 | if (!WIFEXITED(result)) /* abnormally terminated. */
|
---|
115 | rc = -3;
|
---|
116 | if (WEXITSTATUS(result) != 0) /* non-zero exit status. */
|
---|
117 | rc = -4;
|
---|
118 |
|
---|
119 | /* Restore user ID. */
|
---|
120 | setuid(userID);
|
---|
121 |
|
---|
122 | fprintf(stdout, "rc=%d result=%d\n", rc, result);
|
---|
123 | return rc;
|
---|
124 | }
|
---|
125 |
|
---|
126 | /**
|
---|
127 | * Return code legend:
|
---|
128 | * 0 - success.
|
---|
129 | * -1 - no root permission
|
---|
130 | * -2 - invalid USB device path
|
---|
131 | * -3 - failed to acquire devctl_handle
|
---|
132 | * -4 - failed to reset
|
---|
133 | */
|
---|
134 | static int ResetDev(char *pszDevicePath, bool fHardReset)
|
---|
135 | {
|
---|
136 | /* Convert the di_node device path into a devctl compatible Ap-Id. */
|
---|
137 | char szApId[PATH_MAX + 1];
|
---|
138 | char *pszPort = NULL;
|
---|
139 | char *pszDir = NULL;
|
---|
140 | int rc = 0;
|
---|
141 |
|
---|
142 | syslog(LOG_ERR, "VBosUSBHelper: given path=%s\n", pszDevicePath);
|
---|
143 |
|
---|
144 | strcpy(szApId, "/devices");
|
---|
145 | strcat(szApId, pszDevicePath);
|
---|
146 | pszPort = strrchr(pszDevicePath, '@');
|
---|
147 | if (pszPort)
|
---|
148 | {
|
---|
149 | pszPort++;
|
---|
150 | pszDir = strrchr(szApId, '/');
|
---|
151 | if (pszDir)
|
---|
152 | {
|
---|
153 | pszDir[0] = '\0';
|
---|
154 | strcat(szApId, ":");
|
---|
155 | strcat(szApId, pszPort);
|
---|
156 |
|
---|
157 | /* Acquire devctl handle. */
|
---|
158 | devctl_hdl_t pDevHandle;
|
---|
159 | pDevHandle = devctl_ap_acquire(szApId, 0);
|
---|
160 | if (pDevHandle)
|
---|
161 | {
|
---|
162 | /* Prepare to configure. */
|
---|
163 | nvlist_t *pValueList = NULL;
|
---|
164 | nvlist_alloc(&pValueList, NV_UNIQUE_NAME_TYPE, NULL);
|
---|
165 | nvlist_add_int32(pValueList, "port", atoi(pszPort));
|
---|
166 |
|
---|
167 | /* Reset the device */
|
---|
168 | rc = devctl_ap_unconfigure(pDevHandle, pValueList);
|
---|
169 | if (!rc)
|
---|
170 | {
|
---|
171 | if (fHardReset)
|
---|
172 | rc = devctl_ap_disconnect(pDevHandle, pValueList);
|
---|
173 |
|
---|
174 | if (!rc)
|
---|
175 | {
|
---|
176 | if (fHardReset)
|
---|
177 | rc = devctl_ap_connect(pDevHandle, pValueList);
|
---|
178 |
|
---|
179 | if (!rc)
|
---|
180 | {
|
---|
181 | rc = devctl_ap_configure(pDevHandle, pValueList);
|
---|
182 | if (rc)
|
---|
183 | syslog(LOG_ERR, "VBoxUSBHelper: Configuring %s failed. rc=%d errno=%d\n", szApId, rc, errno);
|
---|
184 | }
|
---|
185 | else
|
---|
186 | syslog(LOG_ERR, "VBoxUSBHelper: Connecting %s failed. rc=%d errno=%d\n", szApId, rc, errno);
|
---|
187 | }
|
---|
188 | else
|
---|
189 | syslog(LOG_ERR, "VBoxUSBHelper: Disconnecting for %s failed. rc=%d errno=%d\n", szApId, rc, errno);
|
---|
190 | }
|
---|
191 | else
|
---|
192 | syslog(LOG_ERR, "VBoxUSBHelper: Unconfigure for %s failed. rc=%d errno=%d\n", szApId, rc, errno);
|
---|
193 |
|
---|
194 | if (rc)
|
---|
195 | rc = -4;
|
---|
196 |
|
---|
197 | /* Free-up the name-value list and the obtained handle. */
|
---|
198 | nvlist_free(pValueList);
|
---|
199 | devctl_release(pDevHandle);
|
---|
200 | }
|
---|
201 | else
|
---|
202 | {
|
---|
203 | syslog(LOG_ERR, "VBoxUSBHelper: devctl_ap_acquire for %s failed. errno=%d\n", szApId, errno);
|
---|
204 | rc = -3;
|
---|
205 | }
|
---|
206 | }
|
---|
207 | else
|
---|
208 | {
|
---|
209 | syslog(LOG_ERR, "VBoxUSBHelper: invalid device %s\n", pszDevicePath);
|
---|
210 | rc = -1;
|
---|
211 | }
|
---|
212 | }
|
---|
213 | else
|
---|
214 | {
|
---|
215 | syslog(LOG_ERR, "VBoxUSBHelper: invalid device %s\n", pszDevicePath);
|
---|
216 | rc = -1;
|
---|
217 | }
|
---|
218 | fprintf(stdout, "rc=%d\n", rc);
|
---|
219 | return rc;
|
---|
220 | }
|
---|
221 |
|
---|
222 | /**
|
---|
223 | * returns error code from UpdateDrv. See UpdateDrv error codes.
|
---|
224 | */
|
---|
225 | int main(int argc, char *argv[])
|
---|
226 | {
|
---|
227 | /* Check root permissions. */
|
---|
228 | if (geteuid() != 0)
|
---|
229 | {
|
---|
230 | fprintf(stderr, "This program needs administrator privileges.\n");
|
---|
231 | return -1;
|
---|
232 | }
|
---|
233 |
|
---|
234 | if (argc < 3)
|
---|
235 | {
|
---|
236 | Usage(argv[0]);
|
---|
237 | return -2;
|
---|
238 | }
|
---|
239 |
|
---|
240 | AliasOp Operation = ADD_ALIAS;
|
---|
241 | char *pszOp = argv[1];
|
---|
242 | if (!strcasecmp(pszOp, "hardreset"))
|
---|
243 | return ResetDev(argv[2], true);
|
---|
244 | else if (!strcasecmp(pszOp, "softreset"))
|
---|
245 | return ResetDev(argv[2], false);
|
---|
246 | else if (!strcasecmp(pszOp, "del"))
|
---|
247 | Operation = DEL_ALIAS;
|
---|
248 | else if (!strcasecmp(pszOp, "add"))
|
---|
249 | Operation = ADD_ALIAS;
|
---|
250 | else
|
---|
251 | {
|
---|
252 | Usage(argv[0]);
|
---|
253 | return -2;
|
---|
254 | }
|
---|
255 |
|
---|
256 | char *pszAlias = argv[2];
|
---|
257 | char *pszDrv = argv[3];
|
---|
258 | /* Return status from update_drv */
|
---|
259 | return UpdateDrv(Operation, pszAlias, pszDrv);
|
---|
260 | }
|
---|
261 |
|
---|