Libgourou

Libgourou Git Source Tree

Root/src/device.cpp

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
49int 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 {
81memcpy(mac_address, ifr.ifr_hwaddr.sa_data, 6);
82return 0;
83 }
84
85 return 1;
86}
87#elif BSD_HEADERS
88// https://stackoverflow.com/a/3978293
89int 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
110int 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
126namespace gourou
127{
128 Device::Device(DRMProcessor* processor):
129processor(processor)
130 {}
131
132 Device::Device(DRMProcessor* processor, const std::string& deviceFile, const std::string& deviceKeyFile):
133processor(processor), deviceFile(deviceFile), deviceKeyFile(deviceKeyFile)
134 {
135parseDeviceKeyFile();
136parseDeviceFile();
137 }
138
139 /* SHA1(uid ":" username ":" macaddress ":" */
140 std::string Device::makeSerial(bool random)
141 {
142unsigned char sha_out[SHA1_LEN];
143DRMProcessorClient* client = processor->getClient();
144
145if (!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}
166else
167{
168 client->randBytes(sha_out, sizeof(sha_out));
169}
170
171std::string res = ByteArray((const char*)sha_out, DEVICE_SERIAL_LEN).toHex();
172GOUROU_LOG(DEBUG, "Serial : " << res);
173return res;
174 }
175
176 /* base64(SHA1 (serial + privateKey)) */
177 std::string Device::makeFingerprint(const std::string& serial)
178 {
179DRMProcessorClient* client = processor->getClient();
180unsigned char sha_out[SHA1_LEN];
181
182void* handler = client->createDigest("SHA1");
183client->digestUpdate(handler, (unsigned char*) serial.c_str(), serial.length());
184client->digestUpdate(handler, deviceKey, sizeof(deviceKey));
185client->digestFinalize(handler, sha_out);
186
187std::string res = ByteArray(sha_out, sizeof(sha_out)).toBase64();
188GOUROU_LOG(DEBUG, "Fingerprint : " << res);
189return res;
190 }
191
192 void Device::createDeviceFile(const std::string& hobbes, bool randomSerial)
193 {
194struct utsname sysname;
195uname(&sysname);
196
197std::string serial = makeSerial(randomSerial);
198std::string fingerprint = makeFingerprint(serial);
199
200pugi::xml_document deviceDoc;
201pugi::xml_node decl = deviceDoc.append_child(pugi::node_declaration);
202decl.append_attribute("version") = "1.0";
203
204pugi::xml_node root = deviceDoc.append_child("adept:deviceInfo");
205root.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS;
206
207appendTextElem(root, "adept:deviceClass", "Desktop");
208appendTextElem(root, "adept:deviceSerial", serial);
209appendTextElem(root, "adept:deviceName", sysname.nodename);
210appendTextElem(root, "adept:deviceType", "standalone");
211
212pugi::xml_node version = root.append_child("adept:version");
213version.append_attribute("name") = "hobbes";
214version.append_attribute("value") = hobbes.c_str();
215
216version = root.append_child("adept:version");
217version.append_attribute("name") = "clientOS";
218std::string os = std::string(sysname.sysname) + " " + std::string(sysname.release);
219version.append_attribute("value") = os.c_str();
220
221version = root.append_child("adept:version");
222version.append_attribute("name") = "clientLocale";
223version.append_attribute("value") = setlocale(LC_ALL, NULL);
224
225appendTextElem(root, "adept:fingerprint", fingerprint);
226
227StringXMLWriter xmlWriter;
228deviceDoc.save(xmlWriter, " ");
229
230GOUROU_LOG(DEBUG, "Create device file " << deviceFile);
231
232writeFile(deviceFile, xmlWriter.getResult());
233 }
234
235 void Device::createDeviceKeyFile()
236 {
237unsigned char key[DEVICE_KEY_SIZE];
238
239GOUROU_LOG(DEBUG, "Create device key file " << deviceKeyFile);
240
241processor->getClient()->randBytes(key, sizeof(key));
242
243writeFile(deviceKeyFile, key, sizeof(key));
244 }
245
246 Device* Device::createDevice(DRMProcessor* processor, const std::string& dirName, const std::string& hobbes, bool randomSerial)
247 {
248struct stat _stat;
249
250if (stat(dirName.c_str(), &_stat) != 0)
251{
252 if (mkdir_p(dirName.c_str(), S_IRWXU))
253EXCEPTION(DEV_MKPATH, "Unable to create " << dirName)
254}
255
256Device* device = new Device(processor);
257
258device->deviceFile = dirName + "/device.xml";
259device->deviceKeyFile = dirName + "/devicesalt";
260
261try
262{
263 device->parseDeviceKeyFile();
264}
265catch (...)
266{
267 device->createDeviceKeyFile();
268 device->parseDeviceKeyFile();
269}
270
271try
272{
273 device->parseDeviceFile();
274}
275catch (...)
276{
277 device->createDeviceFile(hobbes, randomSerial);
278 device->parseDeviceFile();
279}
280
281return device;
282 }
283
284 const unsigned char* Device::getDeviceKey()
285 {
286return deviceKey;
287 }
288
289 void Device::parseDeviceFile()
290 {
291pugi::xml_document doc;
292
293if (!doc.load_file(deviceFile.c_str()))
294 EXCEPTION(DEV_INVALID_DEVICE_FILE, "Invalid device file");
295
296try
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 {
309pugi::xml_node node = it->node();
310pugi::xml_attribute name = node.attribute("name");
311pugi::xml_attribute value = node.attribute("value");
312
313properties[name.value()] = value.value();
314 }
315}
316catch (gourou::Exception& e)
317{
318 EXCEPTION(DEV_INVALID_DEVICE_FILE, "Invalid device file");
319}
320 }
321
322 void Device::parseDeviceKeyFile()
323 {
324struct stat _stat;
325
326if (stat(deviceKeyFile.c_str(), &_stat) == 0 &&
327 _stat.st_size == DEVICE_KEY_SIZE)
328{
329 readFile(deviceKeyFile, deviceKey, sizeof(deviceKey));
330}
331else
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 {
337if (properties.find(property) == properties.end())
338{
339 if (_default == "")
340EXCEPTION(DEV_INVALID_DEV_PROPERTY, "Invalid property " << property);
341
342 return _default;
343}
344
345return properties[property];
346 }
347
348 std::string Device::operator[](const std::string& property)
349 {
350return getProperty(property);
351 }
352}

Archive Download this file