Commit | Line | Data |
---|---|---|
970ed795 EL |
1 | /////////////////////////////////////////////////////////////////////////////// |
2 | // Copyright (c) 2000-2014 Ericsson Telecom AB | |
3 | // All rights reserved. This program and the accompanying materials | |
4 | // are made available under the terms of the Eclipse Public License v1.0 | |
5 | // which accompanies this distribution, and is available at | |
6 | // http://www.eclipse.org/legal/epl-v10.html | |
7 | /////////////////////////////////////////////////////////////////////////////// | |
8 | #include <stdio.h> | |
9 | #include <string.h> | |
10 | #include <stdlib.h> | |
11 | #include <unistd.h> | |
12 | #include <time.h> | |
13 | #include <sys/types.h> | |
14 | #include <sys/stat.h> | |
15 | #include <sys/time.h> | |
16 | #ifndef MINGW | |
17 | # include <pwd.h> | |
18 | #endif | |
19 | #include <errno.h> | |
20 | ||
21 | #include <openssl/bn.h> | |
22 | #include <openssl/sha.h> | |
23 | #include <openssl/rand.h> | |
24 | #include <openssl/dsa.h> | |
25 | #include <openssl/pem.h> | |
26 | #include <openssl/err.h> | |
27 | ||
28 | #ifdef WIN32 | |
29 | # include <windows.h> | |
30 | # include <winreg.h> | |
31 | # ifdef MINGW | |
32 | # include <lmcons.h> | |
33 | # endif | |
34 | # include <openssl/md5.h> | |
35 | #endif | |
36 | ||
37 | #ifdef INTERIX | |
38 | # include <interix/interix.h> | |
39 | # include <openssl/md5.h> | |
40 | #endif | |
41 | ||
42 | #include "memory.h" | |
43 | #include "license.h" | |
44 | #include "version.h" | |
45 | ||
46 | #ifdef TTCN3_BUILDNUMBER | |
47 | /* In pre-release builds: */ | |
48 | #define TTCN3_VERSION_CHECK TTCN3_MAJOR * 1000000 + TTCN3_MINOR * 10000 + TTCN3_PATCHLEVEL * 100 + TTCN3_BUILDNUMBER | |
49 | #else | |
50 | /* In official releases: */ | |
51 | #define TTCN3_VERSION_CHECK TTCN3_MAJOR * 10000 + TTCN3_MINOR * 100 + TTCN3_PATCHLEVEL | |
52 | #endif | |
53 | ||
54 | #if TTCN3_VERSION_CHECK != TTCN3_VERSION | |
55 | #error TTCN3_VERSION in version.h does not match the value computed from TTCN3_MAJOR,TTCN3_MINOR,TTCN3_PATCHLEVEL,TTCN3_BUILDNUMBER | |
56 | /* for debugging: these would appear in the preprocessed file */ | |
57 | static const int ttcn3_version = TTCN3_VERSION; | |
58 | static const int ttcn3_version_check = TTCN3_VERSION_CHECK; | |
59 | #endif | |
60 | ||
61 | static const unsigned char dsa_p[] = { 0x90, 0x63, 0x82, 0xae, 0x72, 0x71, 0xac, 0x14, 0xec, 0x9c, 0x71, 0x38, 0x9f, 0xda, 0x8d, 0x52, 0xbd, 0xb1, 0xa7, 0x1a, 0x19, 0xd4, 0x5f, 0xe7, 0x37, 0x3d, 0x44, 0xef, 0xce, 0xa1, 0x99, 0xfa, 0x85, 0xb6, 0x49, 0x77, 0xf1, 0x98, 0x39, 0x6b, 0x71, 0xce, 0x2, 0x42, 0x64, 0x4b, 0xd, 0xad, 0x83, 0xb0, 0x6b, 0x76, 0xba, 0xdc, 0x4f, 0xe0, 0x19, 0xf9, 0xc2, 0x79, 0x6e, 0xbb, 0xc, 0xab, 0x16, 0xae, 0xec, 0x56, 0x75, 0xff, 0x82, 0xb, 0x74, 0xd8, 0x96, 0x42, 0x23, 0x68, 0xf, 0xad, 0x27, 0xee, 0x4c, 0xbf, 0xf2, 0xd4, 0x49, 0x77, 0x8b, 0x1e, 0xf1, 0xdc, 0x5c, 0x4d, 0xfd, 0xa6, 0xd8, 0x5a, 0x70, 0x3f, 0x13, 0xd2, 0xed, 0x3f, 0x59, 0x9, 0x62, 0x2b, 0xb2, 0x8f, 0xcd, 0x7a, 0xa9, 0x3e, 0x6c, 0xb1, 0xe8, 0x80, 0x9d, 0xd2, 0x74, 0xc, 0xc8, 0xdf, 0xa, 0x40, 0xc9, 0xb3 }, | |
62 | dsa_q[] = { 0xa3, 0xe2, 0x23, 0x73, 0xd3, 0x8a, 0x4b, 0x61, 0xd0, 0x60, 0x41, 0x21, 0x41, 0x6d, 0xc4, 0xf6, 0x8c, 0x5c, 0x89, 0x87 }, | |
63 | dsa_g[] = { 0x40, 0x6d, 0xfb, 0x6d, 0xb6, 0x6, 0x32, 0xcc, 0xf0, 0xe9, 0x84, 0x16, 0x1e, 0xe1, 0x21, 0xcd, 0x34, 0xe7, 0xbb, 0x6c, 0x98, 0xff, 0xa9, 0xb9, 0xae, 0xe4, 0x6a, 0x61, 0x51, 0xf8, 0x66, 0x83, 0xa4, 0x34, 0x63, 0x81, 0xc4, 0x5f, 0xee, 0x85, 0x74, 0xee, 0x2a, 0x63, 0x9d, 0xcf, 0x97, 0x50, 0xb8, 0x9f, 0x76, 0xd9, 0xe, 0x58, 0xab, 0xac, 0x2e, 0x23, 0xac, 0x95, 0xc3, 0xb7, 0x14, 0xd6, 0x69, 0xff, 0x36, 0xef, 0xa4, 0xa9, 0xe1, 0xd6, 0x7a, 0xfd, 0x9d, 0x68, 0x91, 0xcf, 0x2d, 0xcd, 0x98, 0xc5, 0xe6, 0xf4, 0x1e, 0xde, 0xf8, 0x65, 0x6b, 0xeb, 0x80, 0x41, 0xab, 0xc7, 0x97, 0xcb, 0xbb, 0xc5, 0x5, 0x7, 0x22, 0x81, 0x58, 0x63, 0xf9, 0x67, 0xd4, 0x7c, 0xb6, 0x21, 0x17, 0xea, 0x62, 0xe3, 0xe8, 0x3f, 0x60, 0xb1, 0x51, 0x51, 0x4, 0xf2, 0x6f, 0x5c, 0x47, 0x69, 0x6b, 0xc1 }, | |
64 | dsa_pub_key[] = { 0x76, 0xe, 0xf, 0x36, 0x77, 0x6, 0x9d, 0xb1, 0xf1, 0x4e, 0x9a, 0x95, 0xae, 0xc9, 0x39, 0xdf, 0x90, 0xd3, 0x94, 0x54, 0xf8, 0xf6, 0x89, 0xc8, 0x11, 0x8d, 0x2e, 0x92, 0x81, 0x6b, 0x2c, 0x37, 0x4, 0x9d, 0x97, 0x9a, 0x43, 0xa3, 0x2e, 0xed, 0x9a, 0x99, 0xb0, 0xcb, 0x9f, 0x4e, 0xea, 0x4f, 0xb7, 0xd5, 0x93, 0xc0, 0x86, 0x1b, 0xc7, 0x97, 0x5f, 0x5, 0xb4, 0xf1, 0xf9, 0xd6, 0x73, 0x97, 0x19, 0xe3, 0xc9, 0xda, 0xfc, 0x39, 0xe0, 0x37, 0xed, 0x7a, 0x62, 0xcb, 0xe3, 0x17, 0x9b, 0x64, 0x3c, 0x46, 0x86, 0x3f, 0x32, 0xec, 0x70, 0xab, 0x5b, 0x87, 0x5a, 0x6e, 0xc3, 0x37, 0xeb, 0x92, 0x58, 0x6c, 0x9e, 0x25, 0x1f, 0x37, 0x4b, 0xcd, 0xb5, 0x22, 0x62, 0x1d, 0x1b, 0x1c, 0xb1, 0x5d, 0xa1, 0xef, 0x50, 0xc3, 0x75, 0xff, 0x2, 0x24, 0x8c, 0xd7, 0x3b, 0x91, 0x77, 0xef, 0x94, 0x76 }; | |
65 | ||
66 | /** Big-endian decoding of a 4-byte integer */ | |
67 | static unsigned int decode_int(const unsigned char *from) | |
68 | { | |
69 | return (unsigned int)from[3] | |
70 | | ((unsigned int)from[2] << 8) | |
71 | | ((unsigned int)from[1] << 16) | |
72 | | ((unsigned int)from[0] << 24); | |
73 | } | |
74 | ||
75 | /** Extract a string from a fixed-length field. | |
76 | * | |
77 | * @param from the beginning of the field | |
78 | * @param max_length the size of the field | |
79 | * @return a newly allocated string. The caller is responsible for | |
80 | * calling Free() | |
81 | * | |
82 | * Verifies that the unused portion of the field is properly zeroed out | |
83 | * (no non-NUL characters after the first NUL character). Terminates | |
84 | * the program with EXIT_FAILURE if there is trailing garbage | |
85 | * after the end of the string. */ | |
86 | static char *decode_string(const char *from, size_t max_length) | |
87 | { | |
88 | size_t i, length; | |
89 | char *ptr; | |
90 | for (i = 0; i < max_length && from[i] != '\0'; i++); | |
91 | length = i; | |
92 | /* Verify that the tail is properly zeroed (no junk). */ | |
93 | for (i++; i < max_length; i++) | |
94 | if (from[i] != '\0') { | |
95 | fputs("License file is corrupted: invalid string encoding\n", | |
96 | stderr); | |
97 | exit(EXIT_FAILURE); | |
98 | } | |
99 | ptr = (char*)Malloc(length + 1); | |
100 | memcpy(ptr, from, length); | |
101 | ptr[length] = '\0'; | |
102 | return ptr; | |
103 | } | |
104 | ||
105 | static void decode_license(license_struct *to, const license_raw *from) | |
106 | { | |
107 | to->license_file = NULL; | |
108 | to->unique_id = decode_int(from->unique_id); | |
109 | to->licensee_name = decode_string(from->licensee_name, | |
110 | sizeof(from->licensee_name)); | |
111 | to->licensee_email = decode_string(from->licensee_email, | |
112 | sizeof(from->licensee_email)); | |
113 | to->licensee_company = decode_string(from->licensee_company, | |
114 | sizeof(from->licensee_company)); | |
115 | to->licensee_department = decode_string(from->licensee_department, | |
116 | sizeof(from->licensee_department)); | |
117 | to->valid_from = decode_int(from->valid_from); | |
118 | to->valid_until = decode_int(from->valid_until); | |
119 | to->host_id = decode_int(from->host_id); | |
120 | to->login_name = decode_string(from->login_name, sizeof(from->login_name)); | |
121 | to->from_major = decode_int(from->from_major); | |
122 | to->from_minor = decode_int(from->from_minor); | |
123 | to->from_patchlevel = decode_int(from->from_patchlevel); | |
124 | to->to_major = decode_int(from->to_major); | |
125 | to->to_minor = decode_int(from->to_minor); | |
126 | to->to_patchlevel = decode_int(from->to_patchlevel); | |
127 | to->feature_list = decode_int(from->feature_list); | |
128 | ||
129 | /* Borrow the PER bit for this year */ | |
130 | /* 1262300400 is Fri Jan 1 00:00:00 2010 */ | |
131 | if((to->feature_list & FEATURE_PER) && (time(NULL) < 1262300400)) { | |
132 | to->feature_list |= FEATURE_XER; | |
133 | } | |
134 | ||
135 | to->limitation_type = decode_int(from->limitation_type); | |
136 | to->max_ptcs = decode_int(from->max_ptcs); | |
137 | } | |
138 | ||
139 | static void check_license_signature(license_raw *lptr) | |
140 | { | |
141 | unsigned char message_digest[SHA_DIGEST_LENGTH]; | |
142 | SHA_CTX sha_ctx; | |
143 | DSA *dsa = DSA_new(); | |
144 | ||
145 | SHA1_Init(&sha_ctx); | |
146 | SHA1_Update(&sha_ctx, lptr, sizeof(*lptr) - sizeof(lptr->dsa_signature)); | |
147 | SHA1_Final(message_digest, &sha_ctx); | |
148 | ||
149 | dsa->p = BN_bin2bn(dsa_p, sizeof(dsa_p), NULL); | |
150 | dsa->q = BN_bin2bn(dsa_q, sizeof(dsa_q), NULL); | |
151 | dsa->g = BN_bin2bn(dsa_g, sizeof(dsa_g), NULL); | |
152 | dsa->pub_key = BN_bin2bn(dsa_pub_key, sizeof(dsa_pub_key), NULL); | |
153 | ||
509718e0 | 154 | // calculate the right len of the signiture |
155 | DSA_SIG *temp_sig=DSA_SIG_new(); | |
156 | int siglen = -1; | |
157 | const unsigned char *data =lptr->dsa_signature; | |
158 | if (temp_sig == NULL || d2i_DSA_SIG(&temp_sig,&data,sizeof(lptr->dsa_signature)) == NULL){ | |
159 | fprintf(stderr, "License signature verification failed: %s\n", | |
160 | ERR_error_string(ERR_get_error(), NULL)); | |
161 | exit(EXIT_FAILURE); | |
162 | } | |
163 | unsigned char *tmp_buff= NULL; | |
164 | siglen = i2d_DSA_SIG(temp_sig, &tmp_buff); | |
165 | OPENSSL_cleanse(tmp_buff, siglen); | |
166 | OPENSSL_free(tmp_buff); | |
167 | DSA_SIG_free(temp_sig); | |
168 | ||
970ed795 | 169 | switch(DSA_verify(0, message_digest, sizeof(message_digest), |
509718e0 | 170 | lptr->dsa_signature, siglen, dsa)) { |
970ed795 EL |
171 | case 0: |
172 | fputs("License file is corrupted: invalid DSA signature.\n", stderr); | |
173 | exit(EXIT_FAILURE); | |
174 | case 1: | |
175 | break; /* valid signature */ | |
176 | default: | |
177 | fprintf(stderr, "License signature verification failed: %s\n", | |
178 | ERR_error_string(ERR_get_error(), NULL)); | |
179 | exit(EXIT_FAILURE); | |
180 | } | |
181 | ||
182 | DSA_free(dsa); | |
183 | } | |
184 | ||
185 | /** Read a PEM-encoded license from a file. | |
186 | * | |
187 | * @param [in] file_name string containing license file name | |
188 | * @param [out] lptr filled with the decoded license information | |
189 | * | |
190 | * If the license information cannot be obtained (bad file name or wrong content), | |
191 | * terminates the program with EXIT_FAILURE. */ | |
192 | static void read_license(const char *file_name, license_raw *lptr) | |
193 | { | |
194 | char *name = NULL; | |
195 | char *header = NULL; | |
196 | unsigned char *data = NULL; | |
197 | long len = 0; | |
198 | struct stat buf; | |
199 | FILE *fp; | |
200 | ||
201 | if (stat(file_name, &buf) != 0) { | |
202 | fprintf(stderr, "Cannot access license file `%s'. (%s)\n", | |
203 | file_name, strerror(errno)); | |
204 | exit(EXIT_FAILURE); | |
205 | } | |
206 | if (buf.st_mode & S_IFDIR) { | |
207 | fprintf(stderr, "The environment variable TTCN3_LICENSE_FILE was set " | |
208 | "to `%s', which is a directory. It must be set to the file that " | |
209 | "contains the license key.\n", file_name); | |
210 | exit(EXIT_FAILURE); | |
211 | } | |
212 | fp = fopen(file_name, "r"); | |
213 | if (fp == NULL) { | |
214 | fprintf(stderr, "Cannot open license file `%s' for reading. (%s)\n", | |
215 | file_name, strerror(errno)); | |
216 | exit(EXIT_FAILURE); | |
217 | } | |
218 | if (!PEM_read(fp, &name, &header, &data, &len)) { | |
219 | fprintf(stderr, "License file is corrupted: %s\n", | |
220 | ERR_error_string(ERR_get_error(), NULL)); | |
221 | exit(EXIT_FAILURE); | |
222 | } | |
223 | fclose(fp); | |
224 | if (strcmp(name, "TTCN-3 LICENSE FILE") || strcmp(header, "")) { | |
225 | fputs("License file is corrupted: invalid header.\n", stderr); | |
226 | exit(EXIT_FAILURE); | |
227 | } | |
228 | if (len != sizeof(*lptr)) { | |
229 | fputs("License file is corrupted: invalid length.\n", stderr); | |
230 | exit(EXIT_FAILURE); | |
231 | } | |
232 | memcpy(lptr, data, sizeof(*lptr)); | |
233 | OPENSSL_free(name); | |
234 | OPENSSL_free(header); | |
235 | OPENSSL_free(data); | |
236 | } | |
237 | ||
238 | void load_license(license_struct *lptr) | |
239 | { | |
240 | const char *file_name = getenv("TTCN3_LICENSE_FILE"); | |
241 | if (file_name == NULL) { | |
242 | fputs("TTCN3_LICENSE_FILE environment variable is not set.\n", | |
243 | stderr); | |
244 | exit(EXIT_FAILURE); | |
245 | } | |
246 | load_license_from_file(lptr, file_name); | |
247 | } | |
248 | ||
249 | void load_license_from_file(license_struct *lptr, const char *file_name) | |
250 | { | |
251 | license_raw lraw; | |
252 | read_license(file_name, &lraw); | |
253 | check_license_signature(&lraw); | |
254 | decode_license(lptr, &lraw); | |
255 | lptr->license_file = mcopystr(file_name); | |
256 | } | |
257 | ||
258 | void free_license(license_struct *lptr) | |
259 | { | |
260 | Free(lptr->license_file); | |
261 | lptr->license_file = NULL; | |
262 | Free(lptr->licensee_name); | |
263 | lptr->licensee_name = NULL; | |
264 | Free(lptr->licensee_email); | |
265 | lptr->licensee_email = NULL; | |
266 | Free(lptr->licensee_company); | |
267 | lptr->licensee_company = NULL; | |
268 | Free(lptr->licensee_department); | |
269 | lptr->licensee_department = NULL; | |
270 | Free(lptr->login_name); | |
271 | lptr->login_name = NULL; | |
272 | } | |
273 | ||
274 | int verify_license(const license_struct *lptr) | |
275 | { | |
276 | time_t current_time = time(NULL); | |
277 | int errflag = 0; | |
278 | ||
279 | if (current_time < lptr->valid_from) { | |
280 | fputs("The license key is not yet valid.\n", stderr); | |
281 | errflag = 1; | |
282 | } | |
283 | ||
284 | if (current_time > lptr->valid_until) { | |
285 | fputs("The license key has already expired.\n", stderr); | |
286 | errflag = 1; | |
287 | } | |
288 | ||
289 | if (lptr->limitation_type & LIMIT_HOST) { | |
290 | /* broken libc call gethostid() performs sign extension on some 64-bit | |
291 | * Linux platforms, thus only the lowest 32 bits are considered */ | |
292 | unsigned long host_id = (unsigned long)gethostid() & 0xffffffffUL; | |
293 | if (host_id != lptr->host_id) { | |
294 | fprintf(stderr, "The license key is not valid for this host " | |
295 | "(%08lx).\n", host_id); | |
296 | errflag = 1; | |
297 | } | |
298 | } | |
299 | ||
300 | if (lptr->limitation_type & LIMIT_USER) { | |
301 | #ifdef MINGW | |
302 | TCHAR user_name[UNLEN + 1]; | |
303 | DWORD buffer_size = sizeof(user_name); | |
304 | if (GetUserName(user_name, &buffer_size)) { | |
305 | if (strcmp(user_name, lptr->login_name)) { | |
306 | fprintf(stderr, "The license key is not valid for this user " | |
307 | "name (%s).\n", user_name); | |
308 | errflag = 1; | |
309 | } | |
310 | } else { | |
311 | fprintf(stderr, "Getting the current user name failed when " | |
312 | "verifying the license key. Windows error code: %d.\n", | |
313 | (int)GetLastError()); | |
314 | errflag = 1; | |
315 | } | |
316 | #else | |
317 | uid_t process_uid = getuid(); | |
318 | struct passwd *p; | |
319 | setpwent(); | |
320 | p = getpwuid(process_uid); | |
321 | if (p == NULL) { | |
322 | fprintf(stderr, "The current user ID (%d) does not have login " | |
323 | "name.\n", (int)process_uid); | |
324 | errflag = 1; | |
325 | } else if (strcmp(p->pw_name, lptr->login_name)) { | |
326 | /* First making a backup copy of the current login name because | |
327 | * the subsequent getpwnam() call will overwrite it. */ | |
328 | char *login_name = mcopystr(p->pw_name); | |
329 | /* Another chance: Trying to map the login name of the license key | |
330 | * to a valid UID. Note that it is possible to associate several | |
331 | * login names with the same UID. */ | |
332 | p = getpwnam(lptr->login_name); | |
333 | if (p == NULL || p->pw_uid != process_uid) { | |
334 | fprintf(stderr, "The license key is not valid for this login " | |
335 | "name (%s).\n", login_name); | |
336 | errflag = 1; | |
337 | } | |
338 | Free(login_name); | |
339 | } | |
340 | endpwent(); | |
341 | #endif | |
342 | } | |
343 | ||
344 | if (TTCN3_MAJOR < lptr->from_major || | |
345 | (TTCN3_MAJOR == lptr->from_major && (TTCN3_MINOR < lptr->from_minor || | |
346 | (TTCN3_MINOR == lptr->from_minor && | |
347 | TTCN3_PATCHLEVEL < lptr->from_patchlevel)))) { | |
348 | /* Checking of to_{major,minor,patchlevel} removed when Titan moved | |
349 | * to major version 2 (licenses were valid up to 1.99pl99 only) */ | |
350 | fputs("The license key is not valid for this version.\n", stderr); | |
351 | errflag = 1; | |
352 | } | |
353 | ||
354 | if (errflag) { | |
355 | return 0; | |
356 | } | |
357 | ||
358 | if (lptr->valid_until - current_time < EXPIRY_WARNING * 86400) { | |
359 | time_t expiry_days = (lptr->valid_until - current_time) / 86400 + 1; | |
360 | fprintf(stderr, "Warning: The license key will expire within %ld " | |
361 | "day%s.\n", (long)expiry_days, expiry_days > 1 ? "s" : ""); | |
362 | } | |
363 | /* setpwent and getpwuid calls may use some system calls that fail. | |
364 | * We should ignore these error codes in future error messages. */ | |
365 | errno = 0; | |
366 | return 1; | |
367 | } | |
368 | ||
369 | unsigned int check_feature(const license_struct *lptr, unsigned int feature) | |
370 | { | |
371 | return (lptr->feature_list & feature) != 0; | |
372 | } | |
373 | ||
374 | void print_license(const license_struct *lptr) | |
375 | { | |
376 | fprintf(stderr, | |
377 | "---------------------------------------------------------------\n" | |
378 | "License file : %s\n" | |
379 | "Unique ID : %d\n" | |
380 | "Licensee : %s\n" | |
381 | "E-mail : %s\n" | |
382 | "Company : %s\n" | |
383 | "Department : %s\n" | |
384 | "Valid from : %s", | |
385 | lptr->license_file != NULL ? lptr->license_file : "", | |
386 | lptr->unique_id, | |
387 | lptr->licensee_name, | |
388 | lptr->licensee_email, | |
389 | lptr->licensee_company, | |
390 | lptr->licensee_department, | |
391 | ctime(&lptr->valid_from)); | |
392 | ||
393 | fprintf(stderr, | |
394 | "Valid until : %s" | |
395 | "Limitation :%s%s\n" | |
396 | "Host ID : %08lx\n" | |
397 | "Login name : %s\n" | |
398 | "Versions : from %d.%d.pl%d until %d.%d.pl%d\n" | |
399 | "Languages :%s%s\n" | |
400 | "Encoders :%s%s%s%s%s\n" | |
401 | "Applications :%s%s%s%s%s%s\n" | |
402 | "Max PTCs : %d\n" | |
403 | "---------------------------------------------------------------\n", | |
404 | ctime(&lptr->valid_until), | |
405 | lptr->limitation_type & LIMIT_HOST ? " HOST" : "", | |
406 | lptr->limitation_type & LIMIT_USER ? " USER" : "", | |
407 | lptr->host_id, | |
408 | lptr->login_name, | |
409 | lptr->from_major, lptr->from_minor, lptr->from_patchlevel, | |
410 | lptr->to_major, lptr->to_minor, lptr->to_patchlevel, | |
411 | lptr->feature_list & FEATURE_TTCN3 ? " TTCN3" : "", | |
412 | lptr->feature_list & FEATURE_ASN1 ? " ASN1" : "", | |
413 | lptr->feature_list & FEATURE_RAW ? " RAW" : "", | |
414 | lptr->feature_list & FEATURE_TEXT ? " TEXT" : "", | |
415 | lptr->feature_list & FEATURE_BER ? " BER" : "", | |
416 | lptr->feature_list & FEATURE_PER ? " PER" : "", | |
417 | lptr->feature_list & FEATURE_XER ? " XER" : "", | |
418 | lptr->feature_list & FEATURE_CODEGEN ? " CODEGEN" : "", | |
419 | lptr->feature_list & FEATURE_TPGEN ? " TPGEN" : "", | |
420 | lptr->feature_list & FEATURE_SINGLE ? " SINGLE" : "", | |
421 | lptr->feature_list & FEATURE_MCTR ? " MCTR" : "", | |
422 | lptr->feature_list & FEATURE_HC ? " HC" : "", | |
423 | lptr->feature_list & FEATURE_LOGFORMAT ? " LOGFORMAT" : "", | |
424 | lptr->max_ptcs); | |
425 | } | |
426 | ||
427 | void print_license_info() | |
428 | { | |
429 | license_struct lstr; | |
430 | int license_valid; | |
431 | fputs("License information:\n", stderr); | |
432 | init_openssl(); | |
433 | load_license(&lstr); | |
434 | print_license(&lstr); | |
435 | license_valid = verify_license(&lstr); | |
436 | free_license(&lstr); | |
437 | free_openssl(); | |
438 | ||
439 | if (!license_valid) { | |
440 | exit(EXIT_FAILURE); | |
441 | } | |
442 | ||
443 | fputs("The license key is valid.\n", stderr); | |
444 | } | |
445 | ||
446 | void init_openssl() | |
447 | { | |
448 | if (!RAND_status()) { | |
449 | time_t time_sec = time(NULL); | |
450 | if (time_sec == (time_t)-1) { | |
451 | perror("time() system call failed"); | |
452 | exit(EXIT_FAILURE); | |
453 | } | |
454 | RAND_seed(&time_sec, sizeof(time_sec)); | |
455 | } | |
456 | while (!RAND_status()) { | |
457 | #ifdef MINGW | |
458 | FILETIME filetime; | |
459 | GetSystemTimeAsFileTime(&filetime); | |
460 | RAND_seed(&filetime.dwLowDateTime, sizeof(filetime.dwLowDateTime)); | |
461 | #else | |
462 | struct timeval tv; | |
463 | if (gettimeofday(&tv, NULL) == -1) { | |
464 | perror("gettimeofday() system call failed"); | |
465 | exit(EXIT_FAILURE); | |
466 | } | |
467 | RAND_seed(&tv.tv_usec, sizeof(tv.tv_usec)); | |
468 | #endif | |
469 | } | |
470 | ERR_load_crypto_strings(); | |
471 | /* Random seeding in OpenSSL may use some system calls that fail. | |
472 | * We should ignore these error codes in future error messages. */ | |
473 | errno = 0; | |
474 | } | |
475 | ||
476 | void free_openssl() | |
477 | { | |
478 | RAND_cleanup(); | |
479 | ERR_free_strings(); | |
480 | } | |
481 | ||
482 | const char * openssl_version_str(void) { | |
483 | return SSLeay_version(SSLEAY_VERSION); | |
484 | } | |
485 | ||
486 | #if defined(WIN32) || defined(INTERIX) | |
487 | ||
488 | #ifdef INTERIX | |
489 | #define INTERIX_PREFIX "\\Registry\\Machine\\" | |
490 | #else | |
491 | #define INTERIX_PREFIX | |
492 | #endif | |
493 | ||
494 | static const char * const keys[] = { | |
495 | INTERIX_PREFIX "Software\\Microsoft\\Windows\\CurrentVersion\\", | |
496 | INTERIX_PREFIX "Software\\Microsoft\\Windows NT\\CurrentVersion\\", | |
497 | NULL | |
498 | }; | |
499 | ||
500 | static const char * const values[] = { | |
501 | /* Product specific info */ | |
502 | "ProductName", | |
503 | "Version", | |
504 | "CurrentVersion", | |
505 | "VersionNumber", | |
506 | "CurrentBuildNumber", | |
507 | /* Unique identifiers */ | |
508 | "ProductKey", | |
509 | "ProductId", | |
510 | "DigitalProductId", | |
511 | /* User specific info */ | |
512 | "RegisteredOwner", | |
513 | "RegisteredOrganization", | |
514 | /* Installation specific info */ | |
515 | "InstallDate", | |
516 | "FirstInstallDateTime", | |
517 | "SystemRoot", | |
518 | NULL | |
519 | }; | |
520 | #endif | |
521 | ||
522 | #ifdef WIN32 | |
523 | ||
524 | long gethostid(void) | |
525 | { | |
526 | const char * const *subKey; | |
527 | MD5_CTX context; | |
528 | unsigned char digest[MD5_DIGEST_LENGTH]; | |
529 | long hostid = 0; | |
530 | unsigned int i; | |
531 | ||
532 | MD5_Init(&context); | |
533 | ||
534 | for (subKey = keys; *subKey != NULL; subKey++) { | |
535 | HKEY hKey; | |
536 | const char * const *valueName; | |
537 | ||
538 | if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, *subKey, | |
539 | 0, KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS) | |
540 | continue; | |
541 | ||
542 | for (valueName = values; *valueName != NULL; valueName++) { | |
543 | DWORD length = 0; | |
544 | unsigned char *buffer; | |
545 | ||
546 | if (RegQueryValueEx(hKey, *valueName, NULL, NULL, NULL, &length) | |
547 | != ERROR_SUCCESS) continue; | |
548 | buffer = (unsigned char*)Malloc(length); | |
549 | if (RegQueryValueEx(hKey, *valueName, NULL, NULL, buffer, &length) | |
550 | == ERROR_SUCCESS) { | |
551 | MD5_Update(&context, buffer, length); | |
552 | } | |
553 | Free(buffer); | |
554 | } | |
555 | RegCloseKey(hKey); | |
556 | } | |
557 | MD5_Final(digest, &context); | |
558 | for (i = 0; i < sizeof(hostid); i++) hostid |= digest[i] << i * 8; | |
559 | return hostid; | |
560 | } | |
561 | ||
562 | #endif | |
563 | ||
564 | #ifdef INTERIX | |
565 | ||
566 | long gethostid(void) | |
567 | { | |
568 | long hostid = 0; | |
569 | const char * const *subKey; | |
570 | size_t i; | |
571 | MD5_CTX context; | |
572 | unsigned char digest[MD5_DIGEST_LENGTH]; | |
573 | ||
574 | MD5_Init(&context); | |
575 | ||
576 | for (subKey = keys; *subKey != NULL; subKey++) { | |
577 | const char * const *valueName; | |
578 | for (valueName = values; *valueName != NULL; valueName++) { | |
579 | int ret = -13, type = -42; | |
580 | size_t size = 0; | |
581 | unsigned char *buffer; | |
582 | char * key = mcopystr(*subKey); | |
583 | key = mputstr(key, *valueName); | |
584 | ||
585 | ret = getreg(key, &type, 0, &size); /* query the size; ret always -1 */ | |
586 | if (size > 0) { | |
587 | buffer = Malloc(size); | |
588 | ret = getreg(key, &type, buffer, &size); | |
589 | if (ret == 0) { | |
590 | switch (type) { | |
591 | case WIN_REG_SZ: | |
592 | case WIN_REG_EXPAND_SZ: | |
593 | case WIN_REG_MULTI_SZ: { | |
594 | /* getreg gave use _wide_ strings. Make them narrow. | |
595 | FIXME this assumes everybody is american and uses US-ASCII. | |
596 | It would be more correct to use iconv() | |
597 | but we don't know which codepage to convert to. | |
598 | */ | |
599 | const unsigned char *from = buffer, *end = buffer + size; | |
600 | unsigned char *to = buffer; | |
601 | for (; from < end; ++from) { /* Yes, from is incremented twice */ | |
602 | *to++ = *from++; | |
603 | } | |
604 | size /= 2; | |
605 | break; } | |
606 | default: | |
607 | break; | |
608 | } | |
609 | MD5_Update(&context, buffer, size); | |
610 | } | |
611 | Free(buffer); | |
612 | } | |
613 | Free(key); | |
614 | } | |
615 | } | |
616 | ||
617 | MD5_Final(digest, &context); | |
618 | for (i = 0; i < sizeof(hostid); i++) hostid |= digest[i] << (i * 8); | |
619 | return hostid; | |
620 | } | |
621 | ||
622 | #endif |