Commit | Line | Data |
---|---|---|
970ed795 | 1 | /////////////////////////////////////////////////////////////////////////////// |
3abe9331 | 2 | // Copyright (c) 2000-2015 Ericsson Telecom AB |
970ed795 EL |
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 <stdarg.h> | |
12 | #include <unistd.h> | |
13 | #include <sys/types.h> | |
14 | #include <sys/time.h> | |
15 | #include <sys/wait.h> | |
16 | ||
17 | #include <mysql.h> | |
18 | ||
19 | #include <openssl/rand.h> | |
20 | #include <openssl/sha.h> | |
21 | #include <openssl/dsa.h> | |
22 | #include <openssl/pem.h> | |
23 | #include <openssl/err.h> | |
24 | ||
25 | #include "../common/memory.h" | |
26 | #include "../common/license.h" | |
27 | ||
28 | #define MYSQL_HOST "mwlx122.eth.ericsson.se" | |
29 | #define MYSQL_USER "ttcn3" | |
30 | #define MYSQL_PASSWORD "ttcn3" | |
31 | #define MYSQL_DATABASE "ttcn3" | |
32 | ||
33 | #define LICENSE_DIR "/mnt/TTCN/license" | |
34 | #define PRIVATE_KEY LICENSE_DIR "/key.pem" | |
35 | ||
36 | #define SECS_IN_DAY 86400 | |
37 | ||
38 | __attribute__ ((__format__ (__printf__, 1, 2), __noreturn__)) | |
39 | static void error(const char *fmt, ...) | |
40 | { | |
41 | va_list pvar; | |
42 | fputs("ERROR: ", stderr); | |
43 | va_start(pvar, fmt); | |
44 | vfprintf(stderr, fmt, pvar); | |
45 | va_end(pvar); | |
46 | fputc('\n', stderr); | |
47 | exit(EXIT_FAILURE); | |
48 | } | |
49 | ||
50 | /* Big-endian */ | |
51 | static void encode_int(unsigned char *to, unsigned int from) | |
52 | { | |
53 | to[3] = from & 0xFF; | |
54 | from >>= 8; | |
55 | to[2] = from & 0xFF; | |
56 | from >>= 8; | |
57 | to[1] = from & 0xFF; | |
58 | from >>= 8; | |
59 | to[0] = from & 0xFF; | |
60 | } | |
61 | ||
62 | static void encode_string(char *to, const char *from, size_t length) | |
63 | { | |
64 | strncpy(to, from, length); | |
65 | } | |
66 | ||
67 | static void encode_license(license_raw *to, const license_struct *from) | |
68 | { | |
69 | memset(to, 0, sizeof(*to)); | |
70 | encode_int(to->unique_id, from->unique_id); | |
71 | encode_string(to->licensee_name, from->licensee_name, | |
72 | sizeof(to->licensee_name)); | |
73 | encode_string(to->licensee_email, from->licensee_email, | |
74 | sizeof(to->licensee_email)); | |
75 | encode_string(to->licensee_company, from->licensee_company, | |
76 | sizeof(to->licensee_company)); | |
77 | encode_string(to->licensee_department, from->licensee_department, | |
78 | sizeof(to->licensee_department)); | |
79 | encode_int(to->valid_from, from->valid_from); | |
80 | encode_int(to->valid_until, from->valid_until); | |
81 | encode_int(to->host_id, from->host_id); | |
82 | encode_string(to->login_name, from->login_name, sizeof(to->login_name)); | |
83 | encode_int(to->from_major, from->from_major); | |
84 | encode_int(to->from_minor, from->from_minor); | |
85 | encode_int(to->from_patchlevel, from->from_patchlevel); | |
86 | encode_int(to->to_major, from->to_major); | |
87 | encode_int(to->to_minor, from->to_minor); | |
88 | encode_int(to->to_patchlevel, from->to_patchlevel); | |
89 | encode_int(to->feature_list, from->feature_list); | |
90 | encode_int(to->limitation_type, from->limitation_type); | |
91 | encode_int(to->max_ptcs, from->max_ptcs); | |
92 | } | |
93 | ||
94 | static void sign_license(license_raw *lptr, const char *dsa_key_file) | |
95 | { | |
96 | unsigned char message_digest[20]; | |
97 | SHA_CTX sha_ctx; | |
98 | unsigned int signature_len = sizeof(lptr->dsa_signature); | |
99 | DSA *dsa; | |
100 | ||
101 | FILE *fp = fopen(dsa_key_file, "r"); | |
102 | if (fp == NULL) { | |
103 | error("Cannot open DSA private key file `%s' for reading: %s", | |
104 | dsa_key_file, strerror(errno)); | |
105 | } | |
106 | ||
107 | dsa = PEM_read_DSAPrivateKey(fp, NULL, NULL, NULL); | |
108 | fclose(fp); | |
109 | ||
110 | if (dsa == NULL) { | |
111 | error("Cannot read DSA private key from `%s': %s", | |
112 | dsa_key_file, ERR_error_string(ERR_get_error(), NULL)); | |
113 | } | |
114 | ||
115 | SHA1_Init(&sha_ctx); | |
116 | SHA1_Update(&sha_ctx, lptr, sizeof(*lptr) - sizeof(lptr->dsa_signature)); | |
117 | SHA1_Final(message_digest, &sha_ctx); | |
118 | ||
119 | if ((int)signature_len != DSA_size(dsa)) { | |
120 | error("Invalid DSA signature size: %d", DSA_size(dsa)); | |
121 | } | |
122 | ||
123 | if (!DSA_sign(0, message_digest, sizeof(message_digest), | |
124 | lptr->dsa_signature, &signature_len, dsa)) { | |
125 | error("DSA signature generation failed: %s", | |
126 | ERR_error_string(ERR_get_error(), NULL)); | |
127 | } | |
128 | ||
129 | DSA_free(dsa); | |
130 | } | |
131 | ||
132 | static void write_license(const char *file_name, const license_raw *lptr) | |
133 | { | |
134 | FILE *fp = fopen(file_name, "w"); | |
135 | if (fp == NULL) { | |
136 | error("Cannot open license file `%s' for writing: %s", file_name, | |
137 | strerror(errno)); | |
138 | } | |
139 | if (!PEM_write(fp, "TTCN-3 LICENSE FILE", "", (unsigned char *)lptr, | |
140 | sizeof(*lptr))) { | |
141 | error("Writing to license file `%s' failed: %s", file_name, | |
142 | ERR_error_string(ERR_get_error(), NULL)); | |
143 | } | |
144 | fclose(fp); | |
145 | } | |
146 | ||
147 | static int privileged_user = 0; | |
148 | static uid_t my_uid = -1; | |
149 | ||
150 | static void check_user(void) | |
151 | { | |
152 | if (geteuid() != 45719) { | |
153 | error("This program must have set-uid to user etccadmi1 (uid 45719)"); | |
154 | } | |
155 | my_uid = getuid(); | |
156 | switch (my_uid) { | |
157 | case 45719: /* etccadmi1 */ | |
158 | privileged_user = 1; | |
159 | case 34217: /* ethjra */ | |
160 | case 34385: /* ethgasz */ | |
161 | break; | |
162 | default: | |
163 | error("You are not allowed to use this program"); | |
164 | } | |
165 | } | |
166 | ||
167 | static void connect_database(MYSQL *mysql) | |
168 | { | |
169 | if (!mysql_init(mysql)) { | |
170 | error("MySQL structure initialization failed"); | |
171 | } | |
172 | if (!mysql_real_connect(mysql, MYSQL_HOST, MYSQL_USER, MYSQL_PASSWORD, | |
173 | MYSQL_DATABASE, 0, NULL, 0)) { | |
174 | error("MySQL database connection failed: %s", mysql_error(mysql)); | |
175 | } else { | |
176 | fputs("Connected to MySQL database.\n", stderr); | |
177 | } | |
178 | } | |
179 | ||
180 | static void disconnect_database(MYSQL *mysql) | |
181 | { | |
182 | mysql_close(mysql); | |
183 | fputs("Disconnected from MySQL database.\n", stderr); | |
184 | } | |
185 | ||
186 | static void fill_from_database_c(MYSQL *mysql, license_struct *plic, int unique_id) | |
187 | { | |
188 | MYSQL_RES *result; | |
189 | my_ulonglong num_rows; | |
190 | unsigned int i, num_fields; | |
191 | MYSQL_FIELD *fields; | |
192 | MYSQL_ROW row; | |
193 | time_t current_time = time(NULL); | |
194 | char *query = mprintf("SELECT * FROM licenses WHERE unique_id = %d", unique_id); | |
195 | if (mysql_query(mysql, query)) { | |
196 | error("Execution of MySQL query `%s' failed: %s", query, | |
197 | mysql_error(mysql)); | |
198 | } | |
199 | Free(query); | |
200 | ||
201 | result = mysql_store_result(mysql); | |
202 | if (result == NULL) { | |
203 | error("MySQL query result fetching failed: %s", mysql_error(mysql)); | |
204 | } | |
205 | ||
206 | num_rows = mysql_num_rows(result); | |
207 | if (num_rows <= 0) { | |
208 | error("There is no license in the database with unique id %d", | |
209 | unique_id); | |
210 | } else if (num_rows > 1) { | |
211 | error("There are %llu licenses in the database with unique id %d", | |
212 | num_rows, unique_id); | |
213 | } | |
214 | ||
215 | num_fields = mysql_num_fields(result); | |
216 | fields = mysql_fetch_fields(result); | |
217 | row = mysql_fetch_row(result); | |
218 | ||
219 | fprintf(stderr, "License data was fetched for unique id %d.\n", unique_id); | |
220 | ||
221 | for (i = 0; i < num_fields; i++) { | |
222 | const char *field_name = fields[i].name; | |
223 | if (!strcmp(field_name, "unique_id")) { | |
224 | plic->unique_id = atoi(row[i]); | |
225 | } else if (!strcmp(field_name, "licensee_name")) { | |
226 | plic->licensee_name = row[i] != NULL ? row[i] : ""; | |
227 | } else if (!strcmp(field_name, "licensee_email")) { | |
228 | plic->licensee_email = row[i] != NULL ? row[i] : ""; | |
229 | } else if (!strcmp(field_name, "contact_name")) { | |
230 | plic->contact_name = row[i] != NULL ? row[i] : ""; | |
231 | } else if (!strcmp(field_name, "contact_email")) { | |
232 | plic->contact_email = row[i] != NULL ? row[i] : ""; | |
233 | } else if (!strcmp(field_name, "send_to")) { | |
234 | if (!strcmp(row[i], "Contact")) plic->send_to = SENDTO_CONTACT; | |
235 | else if (!strcmp(row[i], "Both")) plic->send_to = SENDTO_BOTH; | |
236 | else /*if (!strcmp(row[i], "Licensee"))*/ plic->send_to = SENDTO_LICENSEE; | |
237 | /* SENDTO_LICENSEE is the default */ | |
238 | } else if (!strcmp(field_name, "licensee_company")) { | |
239 | plic->licensee_company = row[i] != NULL ? row[i] : ""; | |
240 | } else if (!strcmp(field_name, "licensee_department")) { | |
241 | plic->licensee_department = row[i] != NULL ? row[i] : ""; | |
242 | } else if (!strcmp(field_name, "valid_from")) { | |
243 | int year, month, day; | |
244 | struct tm tm_struct; | |
245 | if (sscanf(row[i], "%4d-%2d-%2d", &year, &month, &day) != 3) { | |
246 | error("Invalid date format: `%s'", row[i]); | |
247 | } | |
248 | tm_struct.tm_year = year - 1900; | |
249 | tm_struct.tm_mon = month - 1; | |
250 | tm_struct.tm_mday = day; | |
251 | tm_struct.tm_hour = 0; | |
252 | tm_struct.tm_min = 0; | |
253 | tm_struct.tm_sec = 0; | |
254 | tm_struct.tm_isdst = -1; | |
255 | plic->valid_from = mktime(&tm_struct); | |
256 | } else if (!strcmp(field_name, "valid_until")) { | |
257 | int year, month, day; | |
258 | struct tm tm_struct; | |
259 | if (sscanf(row[i], "%4d-%2d-%2d", &year, &month, &day) != 3) { | |
260 | error("Invalid date format: `%s'", row[i]); | |
261 | } | |
262 | tm_struct.tm_year = year - 1900; | |
263 | tm_struct.tm_mon = month - 1; | |
264 | tm_struct.tm_mday = day; | |
265 | tm_struct.tm_hour = 23; | |
266 | tm_struct.tm_min = 59; | |
267 | tm_struct.tm_sec = 59; | |
268 | tm_struct.tm_isdst = -1; | |
269 | plic->valid_until = mktime(&tm_struct); | |
270 | } else if (!strcmp(field_name, "host_id")) { | |
271 | if (row[i] == NULL || sscanf(row[i], "%lx", &plic->host_id) != 1) | |
272 | plic->host_id = 0; | |
273 | } else if (!strcmp(field_name, "login_name")) { | |
274 | plic->login_name = row[i] != NULL ? row[i] : ""; | |
275 | } else if (!strcmp(field_name, "from_major")) { | |
276 | plic->from_major = atoi(row[i]); | |
277 | } else if (!strcmp(field_name, "from_minor")) { | |
278 | plic->from_minor = atoi(row[i]); | |
279 | } else if (!strcmp(field_name, "from_patchlevel")) { | |
280 | plic->from_patchlevel = atoi(row[i]); | |
281 | } else if (!strcmp(field_name, "to_major")) { | |
282 | plic->to_major = atoi(row[i]); | |
283 | } else if (!strcmp(field_name, "to_minor")) { | |
284 | plic->to_minor = atoi(row[i]); | |
285 | } else if (!strcmp(field_name, "to_patchlevel")) { | |
286 | plic->to_patchlevel = atoi(row[i]); | |
287 | } else if (!strcmp(field_name, "feature_ttcn3")) { | |
288 | if (!strcmp(row[i], "Yes")) plic->feature_list |= FEATURE_TTCN3; | |
289 | } else if (!strcmp(field_name, "feature_asn1")) { | |
290 | if (!strcmp(row[i], "Yes")) plic->feature_list |= FEATURE_ASN1; | |
291 | } else if (!strcmp(field_name, "feature_codegen")) { | |
292 | if (!strcmp(row[i], "Yes")) plic->feature_list |= FEATURE_CODEGEN; | |
293 | } else if (!strcmp(field_name, "feature_raw")) { | |
294 | if (!strcmp(row[i], "Yes")) plic->feature_list |= FEATURE_RAW; | |
295 | } else if (!strcmp(field_name, "feature_text")) { | |
296 | if (!strcmp(row[i], "Yes")) plic->feature_list |= FEATURE_TEXT; | |
297 | } else if (!strcmp(field_name, "feature_ber")) { | |
298 | if (!strcmp(row[i], "Yes")) plic->feature_list |= FEATURE_BER; | |
299 | } else if (!strcmp(field_name, "feature_per")) { | |
300 | if (!strcmp(row[i], "Yes")) plic->feature_list |= FEATURE_PER; | |
301 | } else if (!strcmp(field_name, "feature_xer")) { | |
302 | if (!strcmp(row[i], "Yes")) plic->feature_list |= FEATURE_XER; | |
303 | } else if (!strcmp(field_name, "feature_tpgen")) { | |
304 | if (!strcmp(row[i], "Yes")) plic->feature_list |= FEATURE_TPGEN; | |
305 | } else if (!strcmp(field_name, "feature_single")) { | |
306 | if (!strcmp(row[i], "Yes")) plic->feature_list |= FEATURE_SINGLE; | |
307 | } else if (!strcmp(field_name, "feature_mctr")) { | |
308 | if (!strcmp(row[i], "Yes")) plic->feature_list |= FEATURE_MCTR; | |
309 | } else if (!strcmp(field_name, "feature_hc")) { | |
310 | if (!strcmp(row[i], "Yes")) plic->feature_list |= FEATURE_HC; | |
311 | } else if (!strcmp(field_name, "feature_logformat")) { | |
312 | if (!strcmp(row[i], "Yes")) plic->feature_list |= FEATURE_LOGFORMAT; | |
313 | } else if (!strcmp(field_name, "limit_host")) { | |
314 | if (!strcmp(row[i], "Yes")) plic->limitation_type |= LIMIT_HOST; | |
315 | } else if (!strcmp(field_name, "limit_user")) { | |
316 | if (!strcmp(row[i], "Yes")) plic->limitation_type |= LIMIT_USER; | |
317 | } else if (!strcmp(field_name, "max_ptcs")) { | |
318 | plic->max_ptcs = atoi(row[i]); | |
319 | } | |
320 | } /* next */ | |
321 | ||
322 | if (plic->valid_from > current_time) { | |
323 | error("The beginning of validity is in the future: %s", | |
324 | ctime(&plic->valid_from)); | |
325 | } | |
326 | ||
327 | if (plic->valid_until < current_time) { | |
328 | error("The license has already expired on %s", | |
329 | ctime(&plic->valid_until)); | |
330 | } else if (plic->valid_until < current_time + SECS_IN_DAY) { | |
331 | error("The license is valid for less than one day"); | |
332 | } else if (plic->valid_until > current_time + 380 * SECS_IN_DAY && | |
333 | !privileged_user) { | |
334 | error("You are not authorized to generate a license that is valid for " | |
335 | "more than one year (%ld days)", | |
336 | (plic->valid_until - current_time) / SECS_IN_DAY); | |
337 | } | |
338 | ||
339 | if (plic->limitation_type & LIMIT_HOST && plic->host_id == 0) { | |
340 | error("Cannot generate HOST limited license for zero host ID"); | |
341 | } | |
342 | ||
343 | if (plic->limitation_type & LIMIT_USER && !strcmp(plic->login_name, "")) { | |
344 | error("Cannot generate USER limited license for empty login name"); | |
345 | } | |
346 | ||
347 | if (plic->limitation_type == 0 && !privileged_user) { | |
348 | error("You are not authorized to generate unrestricted licenses"); | |
349 | } | |
350 | mysql_free_result(result); | |
351 | } | |
352 | ||
353 | static void fill_from_database(MYSQL *mysql, license_raw *lraw, int unique_id) | |
354 | { | |
355 | ||
356 | license_struct lstr; | |
357 | memset(&lstr, 0, sizeof(lstr)); | |
358 | ||
359 | fill_from_database_c(mysql, &lstr, unique_id); | |
360 | ||
361 | encode_license(lraw, &lstr); | |
362 | ||
363 | } | |
364 | ||
365 | static const char *get_email_address(void) | |
366 | { | |
367 | switch (my_uid) { | |
368 | case 34217: /* ethjra */ | |
369 | return "Julianna.Rozsa@ericsson.com"; | |
370 | case 34385: /* ethgasz */ | |
371 | return "Gabor.Szalai@ericsson.com"; | |
372 | case 45719: /* etccadmi1 */ | |
373 | return "Julianna.Rozsa@ericsson.com"; | |
374 | default: | |
375 | return "???"; | |
376 | } | |
377 | } | |
378 | ||
379 | static const char *get_first_name(void) | |
380 | { | |
381 | switch (my_uid) { | |
382 | case 34217: /* ethjra */ | |
383 | return "Julianna"; | |
384 | case 34385: /* ethgasz */ |