1 | /*␊ |
2 | Copyright 2021 Grégory Soutadé␊ |
3 | ␊ |
4 | This file is part of libgourou.␊ |
5 | ␊ |
6 | libgourou is free software: you can redistribute it and/or modify␊ |
7 | it under the terms of the GNU Lesser General Public License as published by␊ |
8 | the Free Software Foundation, either version 3 of the License, or␊ |
9 | (at your option) any later version.␊ |
10 | ␊ |
11 | libgourou is distributed in the hope that it will be useful,␊ |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of␊ |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the␊ |
14 | GNU Lesser General Public License for more details.␊ |
15 | ␊ |
16 | You should have received a copy of the GNU Lesser General Public License␊ |
17 | along with libgourou. If not, see <http://www.gnu.org/licenses/>.␊ |
18 | */␊ |
19 | ␊ |
20 | #include <sys/stat.h>␊ |
21 | #include <unistd.h>␊ |
22 | #include <sys/types.h>␊ |
23 | #include <sys/utsname.h>␊ |
24 | #include <pwd.h>␊ |
25 | #include <locale.h>␊ |
26 | ␊ |
27 | #include <libgourou.h>␊ |
28 | #include <libgourou_common.h>␊ |
29 | #include <libgourou_log.h>␊ |
30 | #include <device.h>␊ |
31 | ␊ |
32 | #include <string.h>␊ |
33 | #if defined(__linux__) || defined(linux) || defined(__linux)␊ |
34 | #include <sys/ioctl.h>␊ |
35 | #include <net/if.h>␊ |
36 | #include <unistd.h>␊ |
37 | #include <netinet/in.h>␊ |
38 | #elif (defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) \␊ |
39 | || defined(__bsdi__) || defined(__DragonFly__) || defined(__APPLE__))␊ |
40 | #include <ifaddrs.h>␊ |
41 | #include <sys/socket.h>␊ |
42 | #include <net/if_dl.h>␊ |
43 | ␊ |
44 | #define BSD_HEADERS 1␊ |
45 | #endif␊ |
46 | ␊ |
47 | #if defined(__linux__) || defined(linux) || defined(__linux)␊ |
48 | // From https://stackoverflow.com/questions/1779715/how-to-get-mac-address-of-your-machine-using-a-c-program/35242525␊ |
49 | int get_mac_address(unsigned char* mac_address)␊ |
50 | {␊ |
51 | struct ifreq ifr;␊ |
52 | struct ifconf ifc;␊ |
53 | char buf[1024];␊ |
54 | int success = 0;␊ |
55 | ␊ |
56 | int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);␊ |
57 | if (sock == -1) { EXCEPTION(gourou::DEV_MAC_ERROR, "Unable to create socket"); };␊ |
58 | ␊ |
59 | ifc.ifc_len = sizeof(buf);␊ |
60 | ifc.ifc_buf = buf;␊ |
61 | if (ioctl(sock, SIOCGIFCONF, &ifc) == -1) { EXCEPTION(gourou::DEV_MAC_ERROR, "SIOCGIFCONF ioctl failed"); }␊ |
62 | ␊ |
63 | struct ifreq* it = ifc.ifc_req;␊ |
64 | const struct ifreq* const end = it + (ifc.ifc_len / sizeof(struct ifreq));␊ |
65 | ␊ |
66 | for (; it != end; ++it) {␊ |
67 | strcpy(ifr.ifr_name, it->ifr_name);␊ |
68 | if (ioctl(sock, SIOCGIFFLAGS, &ifr) == 0) {␊ |
69 | if (! (ifr.ifr_flags & IFF_LOOPBACK)) { // don't count loopback␊ |
70 | if (ioctl(sock, SIOCGIFHWADDR, &ifr) == 0) {␊ |
71 | success = 1;␊ |
72 | break;␊ |
73 | }␊ |
74 | }␊ |
75 | }␊ |
76 | else { EXCEPTION(gourou::DEV_MAC_ERROR, "SIOCGIFFLAGS ioctl failed"); }␊ |
77 | }␊ |
78 | ␊ |
79 | if (success)␊ |
80 | {␊ |
81 | ␉memcpy(mac_address, ifr.ifr_hwaddr.sa_data, 6);␊ |
82 | ␉return 0;␊ |
83 | }␊ |
84 | ␊ |
85 | return 1;␊ |
86 | }␊ |
87 | #elif BSD_HEADERS␊ |
88 | // https://stackoverflow.com/a/3978293␊ |
89 | int get_mac_address(unsigned char* mac_address, const char* if_name = "en0")␊ |
90 | {␊ |
91 | ifaddrs* iflist;␊ |
92 | int found = 0;␊ |
93 | if (getifaddrs(&iflist) == 0) {␊ |
94 | for (ifaddrs* cur = iflist; cur; cur = cur->ifa_next) {␊ |
95 | if ((cur->ifa_addr->sa_family == AF_LINK) &&␊ |
96 | (strcmp(cur->ifa_name, if_name) == 0) &&␊ |
97 | cur->ifa_addr) {␊ |
98 | sockaddr_dl* sdl = (sockaddr_dl*)cur->ifa_addr;␊ |
99 | memcpy(mac_address, LLADDR(sdl), sdl->sdl_alen);␊ |
100 | found = 1;␊ |
101 | break;␊ |
102 | }␊ |
103 | }␊ |
104 | ␊ |
105 | freeifaddrs(iflist);␊ |
106 | }␊ |
107 | return found;␊ |
108 | }␊ |
109 | #else␊ |
110 | int get_mac_address(unsigned char* mac_address)␊ |
111 | {␊ |
112 | GOUROU_LOG(INFO, "get_mac_address() not implemented for your platform, using a static address");␊ |
113 | ␊ |
114 | mac_address[0] = 0x8D;␊ |
115 | mac_address[1] = 0x70;␊ |
116 | mac_address[2] = 0x13;␊ |
117 | mac_address[3] = 0x8D;␊ |
118 | mac_address[4] = 0x43;␊ |
119 | mac_address[5] = 0x27;␊ |
120 | ␊ |
121 | return 1;␊ |
122 | }␊ |
123 | #endif /* defined(__linux__) || defined(linux) || defined(__linux) */␊ |
124 | ␊ |
125 | ␊ |
126 | namespace gourou␊ |
127 | {␊ |
128 | Device::Device(DRMProcessor* processor):␊ |
129 | ␉processor(processor)␊ |
130 | {}␊ |
131 | ␊ |
132 | Device::Device(DRMProcessor* processor, const std::string& deviceFile, const std::string& deviceKeyFile):␊ |
133 | ␉processor(processor), deviceFile(deviceFile), deviceKeyFile(deviceKeyFile)␊ |
134 | {␊ |
135 | ␉parseDeviceKeyFile();␊ |
136 | ␉parseDeviceFile();␊ |
137 | }␊ |
138 | ␊ |
139 | /* SHA1(uid ":" username ":" macaddress ":" */␊ |
140 | std::string Device::makeSerial(bool random)␊ |
141 | {␊ |
142 | ␉unsigned char sha_out[SHA1_LEN];␊ |
143 | ␉DRMProcessorClient* client = processor->getClient();␊ |
144 | ␉␊ |
145 | ␉if (!random)␊ |
146 | ␉{␊ |
147 | ␉ uid_t uid = getuid();␊ |
148 | ␉ struct passwd * passwd = getpwuid(uid);␊ |
149 | ␉ // Default mac address in case of failure␊ |
150 | ␉ unsigned char mac_address[6] = {0x01, 0x02, 0x03, 0x04, 0x05};␊ |
151 | ␊ |
152 | ␉ get_mac_address(mac_address);␊ |
153 | ␊ |
154 | ␉ int dataToHashLen = 10 /* UID */ + strlen(passwd->pw_name) + sizeof(mac_address)*2 /*mac address*/ + 1 /* \0 */;␊ |
155 | ␉ dataToHashLen += 8; /* Separators */␊ |
156 | ␉ unsigned char* dataToHash = new unsigned char[dataToHashLen];␊ |
157 | ␉ dataToHashLen = snprintf((char*)dataToHash, dataToHashLen, "%d:%s:%02x:%02x:%02x:%02x:%02x:%02x:",␊ |
158 | ␉␉␉␉ uid, passwd->pw_name,␊ |
159 | ␉␉␉␉ mac_address[0], mac_address[1], mac_address[2],␊ |
160 | ␉␉␉␉ mac_address[3], mac_address[4], mac_address[5]);␊ |
161 | ␉␊ |
162 | ␉ client->digest("SHA1", dataToHash, dataToHashLen+1, sha_out);␊ |
163 | ␊ |
164 | ␉ delete[] dataToHash;␊ |
165 | ␉}␊ |
166 | ␉else␊ |
167 | ␉{␊ |
168 | ␉ client->randBytes(sha_out, sizeof(sha_out));␊ |
169 | ␉}␊ |
170 | ␊ |
171 | ␉std::string res = ByteArray((const char*)sha_out, DEVICE_SERIAL_LEN).toHex();␊ |
172 | ␉GOUROU_LOG(DEBUG, "Serial : " << res);␊ |
173 | ␉return res;␊ |
174 | }␊ |
175 | ␊ |
176 | /* base64(SHA1 (serial + privateKey)) */␊ |
177 | std::string Device::makeFingerprint(const std::string& serial)␊ |
178 | {␊ |
179 | ␉DRMProcessorClient* client = processor->getClient();␊ |
180 | ␉unsigned char sha_out[SHA1_LEN];␊ |
181 | ␊ |
182 | ␉void* handler = client->createDigest("SHA1");␊ |
183 | ␉client->digestUpdate(handler, (unsigned char*) serial.c_str(), serial.length());␊ |
184 | ␉client->digestUpdate(handler, deviceKey, sizeof(deviceKey));␊ |
185 | ␉client->digestFinalize(handler, sha_out);␊ |
186 | ␊ |
187 | ␉std::string res = ByteArray(sha_out, sizeof(sha_out)).toBase64();␊ |
188 | ␉GOUROU_LOG(DEBUG, "Fingerprint : " << res);␊ |
189 | ␉return res;␊ |
190 | }␊ |
191 | ␊ |
192 | void Device::createDeviceFile(const std::string& hobbes, bool randomSerial)␊ |
193 | {␊ |
194 | ␉struct utsname sysname;␊ |
195 | ␉uname(&sysname);␊ |
196 | ␊ |
197 | ␉std::string serial = makeSerial(randomSerial);␊ |
198 | ␉std::string fingerprint = makeFingerprint(serial);␊ |
199 | ␉␊ |
200 | ␉pugi::xml_document deviceDoc;␊ |
201 | ␉pugi::xml_node decl = deviceDoc.append_child(pugi::node_declaration);␊ |
202 | ␉decl.append_attribute("version") = "1.0";␊ |
203 | ␉␊ |
204 | ␉pugi::xml_node root = deviceDoc.append_child("adept:deviceInfo");␊ |
205 | ␉root.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS;␊ |
206 | ␊ |
207 | ␉appendTextElem(root, "adept:deviceClass", "Desktop");␊ |
208 | ␉appendTextElem(root, "adept:deviceSerial", serial);␊ |
209 | ␉appendTextElem(root, "adept:deviceName", sysname.nodename);␊ |
210 | ␉appendTextElem(root, "adept:deviceType", "standalone");␊ |
211 | ␊ |
212 | ␉pugi::xml_node version = root.append_child("adept:version");␊ |
213 | ␉version.append_attribute("name") = "hobbes";␊ |
214 | ␉version.append_attribute("value") = hobbes.c_str();␊ |
215 | ␉␊ |
216 | ␉version = root.append_child("adept:version");␊ |
217 | ␉version.append_attribute("name") = "clientOS";␊ |
218 | ␉std::string os = std::string(sysname.sysname) + " " + std::string(sysname.release);␊ |
219 | ␉version.append_attribute("value") = os.c_str();␊ |
220 | ␉␊ |
221 | ␉version = root.append_child("adept:version");␊ |
222 | ␉version.append_attribute("name") = "clientLocale";␊ |
223 | ␉version.append_attribute("value") = setlocale(LC_ALL, NULL);␊ |
224 | ␊ |
225 | ␉appendTextElem(root, "adept:fingerprint", fingerprint);␊ |
226 | ␊ |
227 | ␉StringXMLWriter xmlWriter;␊ |
228 | ␉deviceDoc.save(xmlWriter, " ");␊ |
229 | ␊ |
230 | ␉GOUROU_LOG(DEBUG, "Create device file " << deviceFile);␊ |
231 | ␊ |
232 | ␉writeFile(deviceFile, xmlWriter.getResult());␊ |
233 | }␊ |
234 | ␊ |
235 | void Device::createDeviceKeyFile()␊ |
236 | {␊ |
237 | ␉unsigned char key[DEVICE_KEY_SIZE];␊ |
238 | ␊ |
239 | ␉GOUROU_LOG(DEBUG, "Create device key file " << deviceKeyFile);␊ |
240 | ␊ |
241 | ␉processor->getClient()->randBytes(key, sizeof(key));␊ |
242 | ␊ |
243 | ␉writeFile(deviceKeyFile, key, sizeof(key));␊ |
244 | }␊ |
245 | ␊ |
246 | Device* Device::createDevice(DRMProcessor* processor, const std::string& dirName, const std::string& hobbes, bool randomSerial)␊ |
247 | {␊ |
248 | ␉struct stat _stat;␊ |
249 | ␊ |
250 | ␉if (stat(dirName.c_str(), &_stat) != 0)␊ |
251 | ␉{␊ |
252 | ␉ if (mkdir_p(dirName.c_str(), S_IRWXU))␊ |
253 | ␉␉EXCEPTION(DEV_MKPATH, "Unable to create " << dirName)␊ |
254 | ␉}␊ |
255 | ␊ |
256 | ␉Device* device = new Device(processor);␊ |
257 | ␊ |
258 | ␉device->deviceFile = dirName + "/device.xml";␊ |
259 | ␉device->deviceKeyFile = dirName + "/devicesalt";␊ |
260 | ␊ |
261 | ␉try␊ |
262 | ␉{␊ |
263 | ␉ device->parseDeviceKeyFile();␊ |
264 | ␉}␊ |
265 | ␉catch (...)␊ |
266 | ␉{␊ |
267 | ␉ device->createDeviceKeyFile();␊ |
268 | ␉ device->parseDeviceKeyFile();␊ |
269 | ␉}␊ |
270 | ␊ |
271 | ␉try␊ |
272 | ␉{␊ |
273 | ␉ device->parseDeviceFile();␊ |
274 | ␉}␊ |
275 | ␉catch (...)␊ |
276 | ␉{␊ |
277 | ␉ device->createDeviceFile(hobbes, randomSerial);␊ |
278 | ␉ device->parseDeviceFile();␊ |
279 | ␉}␊ |
280 | ␉␊ |
281 | ␉return device;␊ |
282 | }␊ |
283 | ␊ |
284 | const unsigned char* Device::getDeviceKey()␊ |
285 | {␊ |
286 | ␉return deviceKey;␊ |
287 | }␊ |
288 | ␊ |
289 | void Device::parseDeviceFile()␊ |
290 | {␊ |
291 | ␉pugi::xml_document doc;␊ |
292 | ␊ |
293 | ␉if (!doc.load_file(deviceFile.c_str()))␊ |
294 | ␉ EXCEPTION(DEV_INVALID_DEVICE_FILE, "Invalid device file");␊ |
295 | ␊ |
296 | ␉try␊ |
297 | ␉{␊ |
298 | ␉ properties["deviceClass"] = gourou::extractTextElem(doc, "/adept:deviceInfo/adept:deviceClass");␊ |
299 | ␉ properties["deviceSerial"] = gourou::extractTextElem(doc, "/adept:deviceInfo/adept:deviceSerial");␊ |
300 | ␉ properties["deviceName"] = gourou::extractTextElem(doc, "/adept:deviceInfo/adept:deviceName");␊ |
301 | ␉ properties["deviceType"] = gourou::extractTextElem(doc, "/adept:deviceInfo/adept:deviceType"); ␊ |
302 | ␉ properties["fingerprint"] = gourou::extractTextElem(doc, "/adept:deviceInfo/adept:fingerprint");␊ |
303 | ␊ |
304 | ␉ pugi::xpath_node_set nodeSet = doc.select_nodes("/adept:deviceInfo/adept:version");␊ |
305 | ␊ |
306 | ␉ for (pugi::xpath_node_set::const_iterator it = nodeSet.begin();␊ |
307 | ␉␉ it != nodeSet.end(); ++it)␊ |
308 | ␉ {␊ |
309 | ␉␉pugi::xml_node node = it->node();␊ |
310 | ␉␉pugi::xml_attribute name = node.attribute("name");␊ |
311 | ␉␉pugi::xml_attribute value = node.attribute("value");␊ |
312 | ␊ |
313 | ␉␉properties[name.value()] = value.value();␊ |
314 | ␉ }␊ |
315 | ␉}␊ |
316 | ␉catch (gourou::Exception& e)␊ |
317 | ␉{␊ |
318 | ␉ EXCEPTION(DEV_INVALID_DEVICE_FILE, "Invalid device file");␊ |
319 | ␉}␊ |
320 | }␊ |
321 | ␊ |
322 | void Device::parseDeviceKeyFile()␊ |
323 | {␊ |
324 | ␉struct stat _stat;␊ |
325 | ␊ |
326 | ␉if (stat(deviceKeyFile.c_str(), &_stat) == 0 &&␊ |
327 | ␉ _stat.st_size == DEVICE_KEY_SIZE)␊ |
328 | ␉{␊ |
329 | ␉ readFile(deviceKeyFile, deviceKey, sizeof(deviceKey));␊ |
330 | ␉}␊ |
331 | ␉else␊ |
332 | ␉ EXCEPTION(DEV_INVALID_DEVICE_KEY_FILE, "Invalid device key file");␊ |
333 | }␊ |
334 | ␊ |
335 | std::string Device::getProperty(const std::string& property, const std::string& _default)␊ |
336 | {␊ |
337 | ␉if (properties.find(property) == properties.end())␊ |
338 | ␉{␊ |
339 | ␉ if (_default == "")␊ |
340 | ␉␉EXCEPTION(DEV_INVALID_DEV_PROPERTY, "Invalid property " << property);␊ |
341 | ␊ |
342 | ␉ return _default;␊ |
343 | ␉}␊ |
344 | ␊ |
345 | ␉return properties[property];␊ |
346 | }␊ |
347 | ␊ |
348 | std::string Device::operator[](const std::string& property)␊ |
349 | {␊ |
350 | ␉return getProperty(property);␊ |
351 | }␊ |
352 | }␊ |