1 /*
2 * tvheadend, access control
3 * Copyright (C) 2008 Andreas Öman
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <pthread.h>
20 #include <ctype.h>
21 #include <assert.h>
22 #include <stdio.h>
23 #include <unistd.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdarg.h>
27 #include <fcntl.h>
28 #include <errno.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
31 #include <sys/socket.h>
32
33 #include "tvheadend.h"
34 #include "config.h"
35 #include "access.h"
36 #include "settings.h"
37 #include "channels.h"
38 #include "dvr/dvr.h"
39 #include "tcp.h"
40 #include "lang_codes.h"
41
42 #define TICKET_LIFETIME (5*60) /* in seconds */
43
44 struct access_entry_queue access_entries;
45 struct access_ticket_queue access_tickets;
46 struct passwd_entry_queue passwd_entries;
47 struct ipblock_entry_queue ipblock_entries;
48
49 const char *superuser_username;
50 const char *superuser_password;
51
52 int access_noacl;
53
54 static int passwd_verify(const char *username, verify_callback_t verify, void *aux);
55 static int passwd_verify2(const char *username, verify_callback_t verify, void *aux,
56 const char *username2, const char *passwd2);
57 static void access_ticket_destroy(access_ticket_t *at);
58 static void access_ticket_timeout(void *aux);
59
60 /**
61 *
62 */
63 static void
access_ticket_rearm(void)64 access_ticket_rearm(void)
65 {
66 access_ticket_t *at;
67
68 while ((at = TAILQ_FIRST(&access_tickets)) != NULL) {
69 if (at->at_timer.mti_expire > mclk()) {
70 mtimer_arm_abs(&at->at_timer, access_ticket_timeout, at, at->at_timer.mti_expire);
71 break;
72 }
73 access_ticket_destroy(at);
74 }
75 }
76
77 /**
78 *
79 */
80 static void
access_ticket_destroy(access_ticket_t * at)81 access_ticket_destroy(access_ticket_t *at)
82 {
83 mtimer_disarm(&at->at_timer);
84 free(at->at_id);
85 free(at->at_resource);
86 TAILQ_REMOVE(&access_tickets, at, at_link);
87 access_destroy(at->at_access);
88 free(at);
89 }
90
91 /**
92 *
93 */
94 static access_ticket_t *
access_ticket_find(const char * id)95 access_ticket_find(const char *id)
96 {
97 access_ticket_t *at = NULL;
98
99 if(id != NULL) {
100 /* assume that newer tickets are hit more probably */
101 TAILQ_FOREACH_REVERSE(at, &access_tickets, access_ticket_queue, at_link)
102 if(!strcmp(at->at_id, id))
103 return at;
104 }
105
106 return NULL;
107 }
108
109 /**
110 *
111 */
112 static void
access_ticket_timeout(void * aux)113 access_ticket_timeout(void *aux)
114 {
115 access_ticket_t *at = aux;
116
117 access_ticket_destroy(at);
118 access_ticket_rearm();
119 }
120
121 /**
122 * Create a new ticket for the requested resource and generate a id for it
123 */
124 const char *
access_ticket_create(const char * resource,access_t * a)125 access_ticket_create(const char *resource, access_t *a)
126 {
127 const int64_t lifetime = sec2mono(TICKET_LIFETIME);
128 uint8_t buf[20];
129 char id[41];
130 uint_fast32_t i;
131 access_ticket_t *at;
132 static const char hex_string[16] = "0123456789ABCDEF";
133
134 assert(a);
135
136 /* try to find an existing ticket */
137 TAILQ_FOREACH_REVERSE(at, &access_tickets, access_ticket_queue, at_link) {
138 if (at->at_timer.mti_expire - lifetime + sec2mono(60) < mclk())
139 break;
140 if (strcmp(resource, at->at_resource))
141 continue;
142 if (!access_compare(at->at_access, a))
143 return at->at_id;
144 }
145
146
147 at = calloc(1, sizeof(access_ticket_t));
148
149 uuid_random(buf, 20);
150
151 //convert to hexstring
152 for (i=0; i < sizeof(buf); i++){
153 id[i*2] = hex_string[((buf[i] >> 4) & 0xF)];
154 id[(i*2)+1] = hex_string[(buf[i]) & 0x0F];
155 }
156 id[40] = '\0';
157
158 at->at_id = strdup(id);
159 at->at_resource = strdup(resource);
160
161 at->at_access = access_copy(a);
162 at->at_timer.mti_expire = mclk() + lifetime;
163
164 i = TAILQ_EMPTY(&access_tickets);
165
166 TAILQ_INSERT_TAIL(&access_tickets, at, at_link);
167
168 if (i)
169 access_ticket_rearm();
170
171 return at->at_id;
172 }
173
174 /**
175 *
176 */
177 int
access_ticket_delete(const char * id)178 access_ticket_delete(const char *id)
179 {
180 access_ticket_t *at;
181
182 if((at = access_ticket_find(id)) == NULL)
183 return -1;
184
185 access_ticket_destroy(at);
186 access_ticket_rearm();
187
188 return 0;
189 }
190
191 /**
192 *
193 */
194 access_t *
access_ticket_verify2(const char * id,const char * resource)195 access_ticket_verify2(const char *id, const char *resource)
196 {
197 access_ticket_t *at;
198 char buf[256], *r;
199
200 if((at = access_ticket_find(id)) == NULL)
201 return NULL;
202
203 if (tvheadend_webroot) {
204 snprintf(buf, sizeof(buf), "%s%s", tvheadend_webroot, at->at_resource);
205 r = buf;
206 } else {
207 r = at->at_resource;
208 }
209
210 if(strcmp(r, resource))
211 return NULL;
212
213 return access_copy(at->at_access);
214 }
215
216 /**
217 *
218 */
219 int
access_verify_list(htsmsg_t * list,const char * item)220 access_verify_list(htsmsg_t *list, const char *item)
221 {
222 htsmsg_field_t *f;
223
224 if (list) {
225 HTSMSG_FOREACH(f, list)
226 if (!strcmp(htsmsg_field_get_str(f) ?: "", item))
227 return 0;
228 return -1;
229 }
230 return 0;
231 }
232
233 /**
234 *
235 */
236 int
access_compare(access_t * a,access_t * b)237 access_compare(access_t *a, access_t *b)
238 {
239 int r = strcmp(a->aa_username ?: "", b->aa_username ?: "");
240 if (!r)
241 r = strcmp(a->aa_representative ?: "", b->aa_representative ?: "");
242 return r;
243 }
244
245 /**
246 *
247 */
248 access_t *
access_copy(access_t * src)249 access_copy(access_t *src)
250 {
251 access_t *dst = malloc(sizeof(*dst));
252 *dst = *src;
253 if (src->aa_username)
254 dst->aa_username = strdup(src->aa_username);
255 if (src->aa_representative)
256 dst->aa_representative = strdup(src->aa_representative);
257 if (src->aa_lang)
258 dst->aa_lang = strdup(src->aa_lang);
259 if (src->aa_lang_ui)
260 dst->aa_lang_ui = strdup(src->aa_lang_ui);
261 if (src->aa_theme)
262 dst->aa_theme = strdup(src->aa_theme);
263 if (src->aa_profiles)
264 dst->aa_profiles = htsmsg_copy(src->aa_profiles);
265 if (src->aa_dvrcfgs)
266 dst->aa_dvrcfgs = htsmsg_copy(src->aa_dvrcfgs);
267 if (src->aa_chrange) {
268 size_t l = src->aa_chrange_count * sizeof(uint64_t);
269 dst->aa_chrange = malloc(l);
270 if (dst->aa_chrange == NULL)
271 dst->aa_chrange_count = 0;
272 else
273 memcpy(dst->aa_chrange, src->aa_chrange, l);
274 }
275 if (src->aa_chtags)
276 dst->aa_chtags = htsmsg_copy(src->aa_chtags);
277 return dst;
278 }
279
280 /**
281 *
282 */
283 char *
access_get_lang(access_t * a,const char * lang)284 access_get_lang(access_t *a, const char *lang)
285 {
286 if (lang == NULL) {
287 if (a->aa_lang == NULL)
288 return NULL;
289 return strdup(a->aa_lang);
290 } else {
291 return lang_code_user(lang);
292 }
293 }
294
295 /**
296 *
297 */
298 const char *
access_get_theme(access_t * a)299 access_get_theme(access_t *a)
300 {
301 if (a == NULL)
302 return "blue";
303 if (tvh_str_default(a->aa_theme, NULL) == NULL) {
304 if (tvh_str_default(config.theme_ui, NULL) == NULL)
305 return "blue";
306 return config.theme_ui;
307 }
308 return a->aa_theme;
309 }
310
311 /**
312 *
313 */
314 void
access_destroy(access_t * a)315 access_destroy(access_t *a)
316 {
317 if (a == NULL)
318 return;
319 free(a->aa_username);
320 free(a->aa_representative);
321 free(a->aa_lang);
322 free(a->aa_lang_ui);
323 free(a->aa_theme);
324 free(a->aa_chrange);
325 htsmsg_destroy(a->aa_profiles);
326 htsmsg_destroy(a->aa_dvrcfgs);
327 htsmsg_destroy(a->aa_chtags);
328 free(a);
329 }
330
331 /**
332 *
333 */
334 static int
netmask_verify(struct access_ipmask_queue * ais,struct sockaddr_storage * src)335 netmask_verify(struct access_ipmask_queue *ais, struct sockaddr_storage *src)
336 {
337 access_ipmask_t *ai;
338 int isv4v6 = 0;
339 uint32_t v4v6 = 0;
340
341 if (src->ss_family == AF_INET6) {
342 struct in6_addr *in6 = &(((struct sockaddr_in6 *)src)->sin6_addr);
343 uint32_t *a32 = (uint32_t*)in6->s6_addr;
344 if (a32[0] == 0 && a32[1] == 0 && ntohl(a32[2]) == 0x0000FFFFu) {
345 isv4v6 = 1;
346 v4v6 = ntohl(a32[3]);
347 }
348 }
349
350 TAILQ_FOREACH(ai, ais, ai_link) {
351
352 if (ai->ai_family == AF_INET && src->ss_family == AF_INET) {
353
354 struct sockaddr_in *in4 = (struct sockaddr_in *)src;
355 uint32_t b = ntohl(in4->sin_addr.s_addr);
356 if ((b & ai->ai_netmask) == ai->ai_network)
357 return 1;
358
359 } else if (ai->ai_family == AF_INET && isv4v6) {
360
361 if((v4v6 & ai->ai_netmask) == ai->ai_network)
362 return 1;
363
364 } else if (ai->ai_family == AF_INET6 && isv4v6) {
365
366 continue;
367
368 } else if (ai->ai_family == AF_INET6 && src->ss_family == AF_INET6) {
369
370 struct in6_addr *in6 = &(((struct sockaddr_in6 *)src)->sin6_addr);
371 uint8_t *a8 = (uint8_t*)in6->s6_addr;
372 uint8_t *m8 = (uint8_t*)ai->ai_ip6.s6_addr;
373 int slen = ai->ai_prefixlen;
374 uint32_t apos = 0;
375 uint8_t lastMask = (0xFFu << (8 - (slen % 8)));
376
377 if(slen < 0 || slen > 128)
378 continue;
379
380 while(slen >= 8)
381 {
382 if(a8[apos] != m8[apos])
383 break;
384
385 apos += 1;
386 slen -= 8;
387 }
388 if(slen >= 8)
389 continue;
390
391 if(slen == 0 || (a8[apos] & lastMask) == (m8[apos] & lastMask))
392 return 1;
393 }
394 }
395
396 return 0;
397 }
398
399 /**
400 *
401 */
402 static inline int
access_ip_blocked(struct sockaddr_storage * src)403 access_ip_blocked(struct sockaddr_storage *src)
404 {
405 ipblock_entry_t *ib;
406
407 TAILQ_FOREACH(ib, &ipblock_entries, ib_link)
408 if (ib->ib_enabled && netmask_verify(&ib->ib_ipmasks, src))
409 return 1;
410 return 0;
411 }
412
413 /*
414 *
415 */
416 static void
access_dump_a(access_t * a)417 access_dump_a(access_t *a)
418 {
419 htsmsg_field_t *f;
420 size_t l = 0;
421 char buf[1024];
422 int first;
423
424 tvh_strlcatf(buf, sizeof(buf), l,
425 "%s:%s [%c%c%c%c%c%c%c%c%c%c%c], conn=%u:s%u:r%u:l%u%s",
426 a->aa_representative ?: "<no-id>",
427 a->aa_username ?: "<no-user>",
428 a->aa_rights & ACCESS_STREAMING ? 'S' : ' ',
429 a->aa_rights & ACCESS_ADVANCED_STREAMING ? 'A' : ' ',
430 a->aa_rights & ACCESS_HTSP_STREAMING ? 'T' : ' ',
431 a->aa_rights & ACCESS_WEB_INTERFACE ? 'W' : ' ',
432 a->aa_rights & ACCESS_RECORDER ? 'R' : ' ',
433 a->aa_rights & ACCESS_HTSP_RECORDER ? 'E' : ' ',
434 a->aa_rights & ACCESS_ALL_RECORDER ? 'L' : ' ',
435 a->aa_rights & ACCESS_ALL_RW_RECORDER ? 'D' : ' ',
436 a->aa_rights & ACCESS_FAILED_RECORDER ? 'F' : ' ',
437 a->aa_rights & ACCESS_HTSP_ANONYMIZE ? 'H' : ' ',
438 a->aa_rights & ACCESS_ADMIN ? '*' : ' ',
439 a->aa_conn_limit,
440 a->aa_conn_limit_streaming,
441 a->aa_conn_limit_dvr,
442 a->aa_uilevel,
443 a->aa_match ? ", matched" : "");
444
445 if (a->aa_profiles) {
446 first = 1;
447 HTSMSG_FOREACH(f, a->aa_profiles) {
448 profile_t *pro = profile_find_by_uuid(htsmsg_field_get_str(f) ?: "");
449 if (pro) {
450 if (first)
451 tvh_strlcatf(buf, sizeof(buf), l, ", profile=");
452 tvh_strlcatf(buf, sizeof(buf), l, "%s'%s'",
453 first ? "" : ",", profile_get_name(pro));
454 first = 0;
455 }
456 }
457 } else {
458 tvh_strlcatf(buf, sizeof(buf), l, ", profile=ANY");
459 }
460
461 if (a->aa_dvrcfgs) {
462 first = 1;
463 HTSMSG_FOREACH(f, a->aa_dvrcfgs) {
464 dvr_config_t *cfg = dvr_config_find_by_uuid(htsmsg_field_get_str(f) ?: "");
465 if (cfg) {
466 if (first)
467 tvh_strlcatf(buf, sizeof(buf), l, ", dvr=");
468 tvh_strlcatf(buf, sizeof(buf), l, "%s'%s'",
469 first ? "" : ",", cfg->dvr_config_name ?: "");
470 first = 0;
471 }
472 }
473 } else {
474 tvh_strlcatf(buf, sizeof(buf), l, ", dvr=ANY");
475 }
476
477 if (a->aa_chrange) {
478 for (first = 0; first < a->aa_chrange_count; first += 2)
479 tvh_strlcatf(buf, sizeof(buf), l, ", [chmin=%llu, chmax=%llu]",
480 (long long)a->aa_chrange[first],
481 (long long)a->aa_chrange[first+1]);
482 }
483
484 if (a->aa_chtags) {
485 first = 1;
486 HTSMSG_FOREACH(f, a->aa_chtags) {
487 channel_tag_t *ct = channel_tag_find_by_uuid(htsmsg_field_get_str(f) ?: "");
488 if (ct) {
489 tvh_strlcatf(buf, sizeof(buf), l, "%s'%s'",
490 first ? ", tags=" : ",", ct->ct_name ?: "");
491 first = 0;
492 }
493 }
494 } else {
495 tvh_strlcatf(buf, sizeof(buf), l, ", tag=ANY");
496 }
497
498 tvhtrace(LS_ACCESS, "%s", buf);
499 }
500
501 /*
502 *
503 */
access_alloc(void)504 static access_t *access_alloc(void)
505 {
506 access_t *a = calloc(1, sizeof(access_t));
507 a->aa_uilevel = -1;
508 a->aa_uilevel_nochange = -1;
509 return a;
510 }
511
512 /*
513 *
514 */
access_full(access_t * a)515 static access_t *access_full(access_t *a)
516 {
517 a->aa_rights = ACCESS_FULL;
518 a->aa_uilevel = UILEVEL_EXPERT;
519 a->aa_uilevel_nochange = config.uilevel_nochange;
520 return a;
521 }
522
523 /*
524 *
525 */
526 static void
access_update(access_t * a,access_entry_t * ae)527 access_update(access_t *a, access_entry_t *ae)
528 {
529 idnode_list_mapping_t *ilm;
530 const char *s;
531 char ubuf[UUID_HEX_SIZE];
532
533 if (ae->ae_change_conn_limit) {
534 switch (ae->ae_conn_limit_type) {
535 case ACCESS_CONN_LIMIT_TYPE_ALL:
536 a->aa_conn_limit = ae->ae_conn_limit;
537 case ACCESS_CONN_LIMIT_TYPE_STREAMING:
538 a->aa_conn_limit_streaming = ae->ae_conn_limit;
539 break;
540 case ACCESS_CONN_LIMIT_TYPE_DVR:
541 a->aa_conn_limit_dvr = ae->ae_conn_limit;
542 break;
543 }
544 }
545
546 if (ae->ae_change_uilevel) {
547 a->aa_uilevel = ae->ae_uilevel;
548 a->aa_uilevel_nochange = ae->ae_uilevel_nochange;
549 }
550
551 if (ae->ae_change_chrange) {
552 if (ae->ae_chmin || ae->ae_chmax) {
553 uint64_t *p = realloc(a->aa_chrange, (a->aa_chrange_count + 2) * sizeof(uint64_t));
554 if (p) {
555 p[a->aa_chrange_count++] = ae->ae_chmin;
556 p[a->aa_chrange_count++] = ae->ae_chmax;
557 a->aa_chrange = p;
558 }
559 } else {
560 free(a->aa_chrange);
561 a->aa_chrange = NULL;
562 }
563 }
564
565 if (ae->ae_change_profiles) {
566 if (LIST_EMPTY(&ae->ae_profiles)) {
567 idnode_list_destroy(&ae->ae_profiles, ae);
568 } else {
569 LIST_FOREACH(ilm, &ae->ae_profiles, ilm_in1_link) {
570 profile_t *pro = (profile_t *)ilm->ilm_in2;
571 if(pro && pro->pro_name && pro->pro_name[0] != '\0') {
572 if (a->aa_profiles == NULL)
573 a->aa_profiles = htsmsg_create_list();
574 htsmsg_add_str_exclusive(a->aa_profiles, idnode_uuid_as_str(&pro->pro_id, ubuf));
575 }
576 }
577 }
578 }
579
580 if (ae->ae_change_dvr_configs) {
581 if (LIST_EMPTY(&ae->ae_dvr_configs)) {
582 idnode_list_destroy(&ae->ae_dvr_configs, ae);
583 } else {
584 LIST_FOREACH(ilm, &ae->ae_dvr_configs, ilm_in1_link) {
585 dvr_config_t *dvr = (dvr_config_t *)ilm->ilm_in2;
586 if(dvr && dvr->dvr_config_name[0] != '\0') {
587 if (a->aa_dvrcfgs == NULL)
588 a->aa_dvrcfgs = htsmsg_create_list();
589 htsmsg_add_str_exclusive(a->aa_dvrcfgs, idnode_uuid_as_str(&dvr->dvr_id, ubuf));
590 }
591 }
592 }
593 }
594
595 if (ae->ae_change_chtags) {
596 if (ae->ae_chtags_exclude && !LIST_EMPTY(&ae->ae_chtags)) {
597 channel_tag_t *ct;
598 TAILQ_FOREACH(ct, &channel_tags, ct_link) {
599 if(ct && ct->ct_name[0] != '\0') {
600 LIST_FOREACH(ilm, &ae->ae_chtags, ilm_in1_link) {
601 channel_tag_t *ct2 = (channel_tag_t *)ilm->ilm_in2;
602 if (ct == ct2) break;
603 }
604 if (ilm == NULL) {
605 if (a->aa_chtags == NULL)
606 a->aa_chtags = htsmsg_create_list();
607 htsmsg_add_str_exclusive(a->aa_chtags, idnode_uuid_as_str(&ct->ct_id, ubuf));
608 }
609 }
610 }
611 } else {
612 if (LIST_EMPTY(&ae->ae_chtags)) {
613 idnode_list_destroy(&ae->ae_chtags, ae);
614 } else {
615 LIST_FOREACH(ilm, &ae->ae_chtags, ilm_in1_link) {
616 channel_tag_t *ct = (channel_tag_t *)ilm->ilm_in2;
617 if(ct && ct->ct_name[0] != '\0') {
618 if (a->aa_chtags == NULL)
619 a->aa_chtags = htsmsg_create_list();
620 htsmsg_add_str_exclusive(a->aa_chtags, idnode_uuid_as_str(&ct->ct_id, ubuf));
621 }
622 }
623 }
624 }
625 }
626
627 if (ae->ae_change_lang) {
628 free(a->aa_lang);
629 if (ae->ae_lang && ae->ae_lang[0]) {
630 a->aa_lang = lang_code_user(ae->ae_lang);
631 } else {
632 a->aa_lang = NULL;
633 }
634 }
635
636 if (ae->ae_change_lang_ui) {
637 free(a->aa_lang_ui);
638 if (ae->ae_lang_ui && ae->ae_lang_ui[0])
639 a->aa_lang_ui = lang_code_user(ae->ae_lang_ui);
640 else if ((s = config_get_language_ui()) != NULL)
641 a->aa_lang_ui = lang_code_user(s);
642 else
643 a->aa_lang_ui = NULL;
644 }
645
646 if (ae->ae_change_theme) {
647 free(a->aa_theme);
648 if (ae->ae_theme && ae->ae_theme[0])
649 a->aa_theme = strdup(ae->ae_theme);
650 else
651 a->aa_theme = NULL;
652 }
653
654 if (ae->ae_change_rights) {
655 if (ae->ae_rights == 0)
656 a->aa_rights = 0;
657 else
658 a->aa_rights |= ae->ae_rights;
659 }
660 }
661
662 /**
663 */
664 static void
access_set_lang_ui(access_t * a)665 access_set_lang_ui(access_t *a)
666 {
667 const char *s;
668 if (!a->aa_lang_ui) {
669 if ((s = config_get_language_ui()) != NULL)
670 a->aa_lang_ui = lang_code_user(s);
671 if (a->aa_lang)
672 a->aa_lang_ui = strdup(a->aa_lang);
673 }
674 if (a->aa_uilevel < 0)
675 a->aa_uilevel = config.uilevel;
676 if (a->aa_uilevel_nochange < 0)
677 a->aa_uilevel_nochange = config.uilevel_nochange;
678 }
679
680 /**
681 *
682 */
683 access_t *
access_get(struct sockaddr_storage * src,const char * username,verify_callback_t verify,void * aux)684 access_get(struct sockaddr_storage *src, const char *username, verify_callback_t verify, void *aux)
685 {
686 access_t *a = access_alloc();
687 access_entry_t *ae;
688 int nouser = tvh_str_default(username, NULL) == NULL;
689
690 if (!access_noacl && access_ip_blocked(src))
691 return a;
692
693 if (!passwd_verify(username, verify, aux)) {
694 a->aa_username = strdup(username);
695 a->aa_representative = strdup(username);
696 if(!passwd_verify2(username, verify, aux,
697 superuser_username, superuser_password))
698 return access_full(a);
699 } else {
700 a->aa_representative = malloc(50);
701 tcp_get_str_from_ip(src, a->aa_representative, 50);
702 if(!passwd_verify2(username, verify, aux,
703 superuser_username, superuser_password))
704 return access_full(a);
705 username = NULL;
706 }
707
708 if (access_noacl)
709 return access_full(a);
710
711 TAILQ_FOREACH(ae, &access_entries, ae_link) {
712
713 if(!ae->ae_enabled)
714 continue;
715
716 if(ae->ae_username[0] != '*') {
717 /* acl entry requires username to match */
718 if(username == NULL || strcmp(username, ae->ae_username))
719 continue; /* Didn't get one */
720 }
721
722 if(!netmask_verify(&ae->ae_ipmasks, src))
723 continue; /* IP based access mismatches */
724
725 if(ae->ae_username[0] != '*')
726 a->aa_match = 1;
727
728 access_update(a, ae);
729 }
730
731 /* Username was not matched - no access */
732 if (!a->aa_match) {
733 free(a->aa_username);
734 a->aa_username = NULL;
735 if (!nouser)
736 a->aa_rights = 0;
737 }
738
739 access_set_lang_ui(a);
740
741 if (tvhtrace_enabled())
742 access_dump_a(a);
743 return a;
744 }
745
746 /**
747 *
748 */
749 access_t *
access_get_by_username(const char * username)750 access_get_by_username(const char *username)
751 {
752 access_t *a = access_alloc();
753 access_entry_t *ae;
754
755 a->aa_username = strdup(username);
756 a->aa_representative = strdup(username);
757
758 if(access_noacl)
759 return access_full(a);
760
761 if (username[0] == '\0')
762 return a;
763
764 TAILQ_FOREACH(ae, &access_entries, ae_link) {
765
766 if(!ae->ae_enabled)
767 continue;
768
769 if(ae->ae_username[0] == '*' || strcmp(ae->ae_username, username))
770 continue;
771
772 access_update(a, ae);
773 }
774
775 access_set_lang_ui(a);
776
777 return a;
778 }
779
780 /**
781 *
782 */
783 access_t *
access_get_by_addr(struct sockaddr_storage * src)784 access_get_by_addr(struct sockaddr_storage *src)
785 {
786 access_t *a = access_alloc();
787 access_entry_t *ae;
788
789 a->aa_representative = malloc(50);
790 tcp_get_str_from_ip(src, a->aa_representative, 50);
791
792 if(access_noacl)
793 return access_full(a);
794
795 if (access_ip_blocked(src))
796 return a;
797
798 TAILQ_FOREACH(ae, &access_entries, ae_link) {
799
800 if(!ae->ae_enabled)
801 continue;
802
803 if(ae->ae_username[0] != '*')
804 continue;
805
806 if(!netmask_verify(&ae->ae_ipmasks, src))
807 continue; /* IP based access mismatches */
808
809 access_update(a, ae);
810 }
811
812 access_set_lang_ui(a);
813
814 return a;
815 }
816
817 /**
818 *
819 */
820 static void
access_set_prefix_default(struct access_ipmask_queue * ais)821 access_set_prefix_default(struct access_ipmask_queue *ais)
822 {
823 access_ipmask_t *ai;
824
825 ai = calloc(1, sizeof(access_ipmask_t));
826 ai->ai_family = AF_INET6;
827 TAILQ_INSERT_HEAD(ais, ai, ai_link);
828
829 ai = calloc(1, sizeof(access_ipmask_t));
830 ai->ai_family = AF_INET;
831 TAILQ_INSERT_HEAD(ais, ai, ai_link);
832 }
833
834 /**
835 *
836 */
access_addr4_empty(const char * s)837 static int access_addr4_empty(const char *s)
838 {
839 int empty = 1;
840 while (*s) {
841 if (*s == '0') {
842 /* nothing */
843 } else if (isdigit(*s)) {
844 empty = 0;
845 } else if (*s == '.') {
846 empty = 0;
847 } else {
848 return 1;
849 }
850 s++;
851 }
852 return empty;
853 }
854
855 /**
856 *
857 */
access_addr6_empty(const char * s)858 static int access_addr6_empty(const char *s)
859 {
860 int empty = 1;
861 while (*s) {
862 if (*s == '0') {
863 /* nothing */
864 } else if (isdigit(*s)) {
865 empty = 0;
866 } else if (*s == ':') {
867 empty = 0;
868 } else {
869 return 1;
870 }
871 s++;
872 }
873 return empty;
874 }
875
876 /**
877 *
878 */
879 static void
access_set_prefix(struct access_ipmask_queue * ais,const char * prefix,int dflt)880 access_set_prefix(struct access_ipmask_queue *ais, const char *prefix, int dflt)
881 {
882 static const char *delim = ",;| ";
883 char buf[100];
884 char tokbuf[4096];
885 int prefixlen;
886 char *p, *tok, *saveptr;
887 in_addr_t s_addr;
888 access_ipmask_t *ai = NULL;
889
890 while((ai = TAILQ_FIRST(ais)) != NULL) {
891 TAILQ_REMOVE(ais, ai, ai_link);
892 free(ai);
893 }
894
895 strlcpy(tokbuf, prefix, sizeof(tokbuf));
896 tok = strtok_r(tokbuf, delim, &saveptr);
897
898 while (tok != NULL) {
899 if (ai == NULL)
900 ai = calloc(1, sizeof(access_ipmask_t));
901
902 if (strlen(tok) > sizeof(buf) - 1 || *tok == '\0')
903 goto fnext;
904
905 strcpy(buf, tok);
906
907 if (strchr(buf, ':') != NULL)
908 ai->ai_family = AF_INET6;
909 else
910 ai->ai_family = AF_INET;
911
912 if (ai->ai_family == AF_INET6) {
913 if ((p = strchr(buf, '/')) != NULL) {
914 *p++ = 0;
915 prefixlen = atoi(p);
916 if (prefixlen < 0 || prefixlen > 128)
917 goto fnext;
918 } else {
919 prefixlen = !access_addr6_empty(buf) ? 128 : 0;
920 }
921
922 ai->ai_prefixlen = prefixlen;
923 inet_pton(AF_INET6, buf, &ai->ai_ip6);
924
925 ai->ai_netmask = 0xffffffff;
926 ai->ai_network = 0x00000000;
927 } else {
928 if ((p = strchr(buf, '/')) != NULL) {
929 *p++ = 0;
930 prefixlen = atoi(p);
931 if (prefixlen < 0 || prefixlen > 32)
932 goto fnext;
933 } else {
934 prefixlen = !access_addr4_empty(buf) ? 32 : 0;
935 }
936
937 s_addr = inet_addr(buf);
938 ai->ai_prefixlen = prefixlen;
939
940 ai->ai_netmask = prefixlen ? 0xffffffff << (32 - prefixlen) : 0;
941 ai->ai_network = ntohl(s_addr) & ai->ai_netmask;
942 }
943
944 TAILQ_INSERT_TAIL(ais, ai, ai_link);
945 ai = NULL;
946
947 tok = strtok_r(NULL, delim, &saveptr);
948 continue;
949
950 fnext:
951 tok = strtok_r(NULL, delim, &saveptr);
952 if (tok == NULL) {
953 free(ai);
954 ai = NULL;
955 }
956 }
957
958 if (dflt && !TAILQ_FIRST(ais))
959 access_set_prefix_default(ais);
960 }
961
962 /**
963 *
964 */
access_get_prefix(struct access_ipmask_queue * ais)965 static const char *access_get_prefix(struct access_ipmask_queue *ais)
966 {
967 char addrbuf[50];
968 access_ipmask_t *ai;
969 size_t pos = 0;
970 uint32_t s_addr;
971
972 prop_sbuf[0] = prop_sbuf[1] = '\0';
973 TAILQ_FOREACH(ai, ais, ai_link) {
974 if(PROP_SBUF_LEN-pos <= 0)
975 break;
976 if(ai->ai_family == AF_INET6) {
977 inet_ntop(AF_INET6, &ai->ai_ip6, addrbuf, sizeof(addrbuf));
978 } else {
979 s_addr = htonl(ai->ai_network);
980 inet_ntop(AF_INET, &s_addr, addrbuf, sizeof(addrbuf));
981 }
982 tvh_strlcatf(prop_sbuf, PROP_SBUF_LEN, pos, ",%s/%d", addrbuf, ai->ai_prefixlen);
983 }
984 return prop_sbuf + 1;
985 }
986
987 /**
988 *
989 */
990 static void
access_entry_update_rights(access_entry_t * ae)991 access_entry_update_rights(access_entry_t *ae)
992 {
993 uint32_t r = 0;
994
995 if (ae->ae_streaming)
996 r |= ACCESS_STREAMING;
997 if (ae->ae_adv_streaming)
998 r |= ACCESS_ADVANCED_STREAMING;
999 if (ae->ae_htsp_streaming)
1000 r |= ACCESS_HTSP_STREAMING;
1001 if (ae->ae_webui)
1002 r |= ACCESS_WEB_INTERFACE;
1003 if (ae->ae_dvr)
1004 r |= ACCESS_RECORDER;
1005 if (ae->ae_htsp_dvr)
1006 r |= ACCESS_HTSP_RECORDER;
1007 if (ae->ae_all_dvr)
1008 r |= ACCESS_ALL_RECORDER;
1009 if (ae->ae_all_rw_dvr)
1010 r |= ACCESS_ALL_RW_RECORDER;
1011 if (ae->ae_failed_dvr)
1012 r |= ACCESS_FAILED_RECORDER;
1013 if (ae->ae_htsp_anonymize)
1014 r |= ACCESS_HTSP_ANONYMIZE;
1015 if (ae->ae_admin)
1016 r |= ACCESS_ADMIN;
1017 ae->ae_rights = r;
1018 }
1019
1020 /**
1021 *
1022 */
1023
1024 static void access_entry_reindex(void);
1025
1026 access_entry_t *
access_entry_create(const char * uuid,htsmsg_t * conf)1027 access_entry_create(const char *uuid, htsmsg_t *conf)
1028 {
1029 access_entry_t *ae, *ae2;
1030
1031 lock_assert(&global_lock);
1032
1033 ae = calloc(1, sizeof(access_entry_t));
1034
1035 if (idnode_insert(&ae->ae_id, uuid, &access_entry_class, 0)) {
1036 if (uuid)
1037 tvherror(LS_ACCESS, "invalid uuid '%s'", uuid);
1038 free(ae);
1039 return NULL;
1040 }
1041
1042 TAILQ_INIT(&ae->ae_ipmasks);
1043
1044 ae->ae_uilevel = UILEVEL_DEFAULT;
1045 ae->ae_uilevel_nochange = -1;
1046
1047 if (conf) {
1048 /* defaults */
1049 ae->ae_change_lang = 1;
1050 ae->ae_change_lang_ui = 1;
1051 ae->ae_change_theme = 1;
1052 ae->ae_change_uilevel = 1;
1053 ae->ae_change_profiles = 1;
1054 ae->ae_change_conn_limit = 1;
1055 ae->ae_change_dvr_configs = 1;
1056 ae->ae_change_chrange = 1;
1057 ae->ae_change_chtags = 1;
1058 ae->ae_change_rights = 1;
1059 ae->ae_htsp_streaming = 1;
1060 ae->ae_htsp_dvr = 1;
1061 ae->ae_all_dvr = 1;
1062 ae->ae_failed_dvr = 1;
1063 idnode_load(&ae->ae_id, conf);
1064 access_entry_update_rights(ae);
1065 TAILQ_FOREACH(ae2, &access_entries, ae_link)
1066 if (ae->ae_index < ae2->ae_index)
1067 break;
1068 if (ae2)
1069 TAILQ_INSERT_BEFORE(ae2, ae, ae_link);
1070 else
1071 TAILQ_INSERT_TAIL(&access_entries, ae, ae_link);
1072 } else {
1073 TAILQ_INSERT_TAIL(&access_entries, ae, ae_link);
1074 access_entry_reindex();
1075 }
1076
1077 if (ae->ae_username == NULL)
1078 ae->ae_username = strdup("*");
1079 if (ae->ae_comment == NULL)
1080 ae->ae_comment = strdup("New entry");
1081 if (TAILQ_FIRST(&ae->ae_ipmasks) == NULL)
1082 access_set_prefix_default(&ae->ae_ipmasks);
1083
1084 return ae;
1085 }
1086
1087 /**
1088 *
1089 */
1090 void
access_entry_destroy(access_entry_t * ae,int delconf)1091 access_entry_destroy(access_entry_t *ae, int delconf)
1092 {
1093 access_ipmask_t *ai;
1094 char ubuf[UUID_HEX_SIZE];
1095
1096 idnode_save_check(&ae->ae_id, delconf);
1097
1098 if (delconf)
1099 hts_settings_remove("accesscontrol/%s", idnode_uuid_as_str(&ae->ae_id, ubuf));
1100
1101 TAILQ_REMOVE(&access_entries, ae, ae_link);
1102 idnode_unlink(&ae->ae_id);
1103
1104 idnode_list_destroy(&ae->ae_profiles, ae);
1105 idnode_list_destroy(&ae->ae_dvr_configs, ae);
1106 idnode_list_destroy(&ae->ae_chtags, ae);
1107
1108 while((ai = TAILQ_FIRST(&ae->ae_ipmasks)) != NULL)
1109 {
1110 TAILQ_REMOVE(&ae->ae_ipmasks, ai, ai_link);
1111 free(ai);
1112 }
1113
1114 free(ae->ae_username);
1115 free(ae->ae_comment);
1116 free(ae->ae_lang);
1117 free(ae->ae_lang_ui);
1118 free(ae->ae_theme);
1119 free(ae);
1120 }
1121
1122 /*
1123 *
1124 */
1125 void
access_destroy_by_profile(profile_t * pro,int delconf)1126 access_destroy_by_profile(profile_t *pro, int delconf)
1127 {
1128 idnode_list_destroy(&pro->pro_accesses, delconf ? pro : NULL);
1129 }
1130
1131 /*
1132 *
1133 */
1134 void
access_destroy_by_dvr_config(dvr_config_t * cfg,int delconf)1135 access_destroy_by_dvr_config(dvr_config_t *cfg, int delconf)
1136 {
1137 idnode_list_destroy(&cfg->dvr_accesses, delconf ? cfg : NULL);
1138 }
1139
1140 /*
1141 *
1142 */
1143 void
access_destroy_by_channel_tag(channel_tag_t * ct,int delconf)1144 access_destroy_by_channel_tag(channel_tag_t *ct, int delconf)
1145 {
1146 idnode_list_destroy(&ct->ct_accesses, delconf ? ct : NULL);
1147 }
1148
1149 /**
1150 *
1151 */
1152 static void
access_entry_reindex(void)1153 access_entry_reindex(void)
1154 {
1155 access_entry_t *ae;
1156 int i = 1;
1157
1158 TAILQ_FOREACH(ae, &access_entries, ae_link) {
1159 if (ae->ae_index != i) {
1160 ae->ae_index = i;
1161 idnode_changed(&ae->ae_id);
1162 }
1163 i++;
1164 }
1165 }
1166
1167 /* **************************************************************************
1168 * Class definition
1169 * **************************************************************************/
1170
1171 static htsmsg_t *
access_entry_class_save(idnode_t * self,char * filename,size_t fsize)1172 access_entry_class_save(idnode_t *self, char *filename, size_t fsize)
1173 {
1174 access_entry_t *ae = (access_entry_t *)self;
1175 char ubuf[UUID_HEX_SIZE];
1176 htsmsg_t *c = htsmsg_create_map();
1177 access_entry_update_rights((access_entry_t *)self);
1178 idnode_save(&ae->ae_id, c);
1179 snprintf(filename, fsize, "accesscontrol/%s", idnode_uuid_as_str(&ae->ae_id, ubuf));
1180 return c;
1181 }
1182
1183 static void
access_entry_class_delete(idnode_t * self)1184 access_entry_class_delete(idnode_t *self)
1185 {
1186 access_entry_t *ae = (access_entry_t *)self;
1187 access_entry_destroy(ae, 1);
1188 }
1189
1190 static void
access_entry_class_moveup(idnode_t * self)1191 access_entry_class_moveup(idnode_t *self)
1192 {
1193 access_entry_t *ae = (access_entry_t *)self;
1194 access_entry_t *prev = TAILQ_PREV(ae, access_entry_queue, ae_link);
1195 if (prev) {
1196 TAILQ_REMOVE(&access_entries, ae, ae_link);
1197 TAILQ_INSERT_BEFORE(prev, ae, ae_link);
1198 access_entry_reindex();
1199 }
1200 }
1201
1202 static void
access_entry_class_movedown(idnode_t * self)1203 access_entry_class_movedown(idnode_t *self)
1204 {
1205 access_entry_t *ae = (access_entry_t *)self;
1206 access_entry_t *next = TAILQ_NEXT(ae, ae_link);
1207 if (next) {
1208 TAILQ_REMOVE(&access_entries, ae, ae_link);
1209 TAILQ_INSERT_AFTER(&access_entries, next, ae, ae_link);
1210 access_entry_reindex();
1211 }
1212 }
1213
1214 static const char *
access_entry_class_get_title(idnode_t * self,const char * lang)1215 access_entry_class_get_title (idnode_t *self, const char *lang)
1216 {
1217 access_entry_t *ae = (access_entry_t *)self;
1218 const char *s = ae->ae_username;
1219
1220 if (ae->ae_comment && ae->ae_comment[0] != '\0') {
1221 if (ae->ae_username && ae->ae_username[0]) {
1222 snprintf(prop_sbuf, PROP_SBUF_LEN, "%s (%s)", ae->ae_username, ae->ae_comment);
1223 s = prop_sbuf;
1224 } else {
1225 s = ae->ae_comment;
1226 }
1227 }
1228 if (s == NULL || *s == '\0')
1229 s = "";
1230 return s;
1231 }
1232
1233 static int
access_entry_class_prefix_set(void * o,const void * v)1234 access_entry_class_prefix_set(void *o, const void *v)
1235 {
1236 access_set_prefix(&((access_entry_t *)o)->ae_ipmasks, (const char *)v, 1);
1237 return 1;
1238 }
1239
1240 static const void *
access_entry_class_prefix_get(void * o)1241 access_entry_class_prefix_get(void *o)
1242 {
1243 prop_ptr = access_get_prefix(&((access_entry_t *)o)->ae_ipmasks);
1244 return &prop_ptr;
1245 }
1246
1247 static int
access_entry_chtag_set_cb(idnode_t * in1,idnode_t * in2,void * origin)1248 access_entry_chtag_set_cb ( idnode_t *in1, idnode_t *in2, void *origin )
1249 {
1250 access_entry_t *ae = (access_entry_t *)in1;
1251 idnode_list_mapping_t *ilm;
1252 channel_tag_t *ct = (channel_tag_t *)in2;
1253 ilm = idnode_list_link(in1, &ae->ae_chtags, in2, &ct->ct_accesses, origin, 1);
1254 return ilm ? 1 : 0;
1255 }
1256
1257 static int
access_entry_chtag_set(void * o,const void * v)1258 access_entry_chtag_set(void *o, const void *v)
1259 {
1260 access_entry_t *ae = (access_entry_t *)o;
1261 return idnode_list_set1(&ae->ae_id, &ae->ae_chtags,
1262 &channel_tag_class, (htsmsg_t *)v,
1263 access_entry_chtag_set_cb);
1264 }
1265
1266 static const void *
access_entry_chtag_get(void * o)1267 access_entry_chtag_get(void *o)
1268 {
1269 return idnode_list_get1(&((access_entry_t *)o)->ae_chtags);
1270 }
1271
1272 static char *
access_entry_chtag_rend(void * o,const char * lang)1273 access_entry_chtag_rend (void *o, const char *lang)
1274 {
1275 return idnode_list_get_csv1(&((access_entry_t *)o)->ae_chtags, lang);
1276 }
1277
1278 static int
access_entry_dvr_config_set_cb(idnode_t * in1,idnode_t * in2,void * origin)1279 access_entry_dvr_config_set_cb ( idnode_t *in1, idnode_t *in2, void *origin )
1280 {
1281 access_entry_t *ae = (access_entry_t *)in1;
1282 idnode_list_mapping_t *ilm;
1283 dvr_config_t *dvr = (dvr_config_t *)in2;
1284 ilm = idnode_list_link(in1, &ae->ae_dvr_configs, in2, &dvr->dvr_accesses, origin, 1);
1285 return ilm ? 1 : 0;
1286 }
1287
1288 static int
access_entry_dvr_config_set(void * o,const void * v)1289 access_entry_dvr_config_set(void *o, const void *v)
1290 {
1291 access_entry_t *ae = (access_entry_t *)o;
1292 return idnode_list_set1(&ae->ae_id, &ae->ae_dvr_configs,
1293 &dvr_config_class, (htsmsg_t *)v,
1294 access_entry_dvr_config_set_cb);
1295 }
1296
1297 static const void *
access_entry_dvr_config_get(void * o)1298 access_entry_dvr_config_get(void *o)
1299 {
1300 return idnode_list_get1(&((access_entry_t *)o)->ae_dvr_configs);
1301 }
1302
1303 static char *
access_entry_dvr_config_rend(void * o,const char * lang)1304 access_entry_dvr_config_rend (void *o, const char *lang)
1305 {
1306 return idnode_list_get_csv1(&((access_entry_t *)o)->ae_dvr_configs, lang);
1307 }
1308
1309 static int
access_entry_profile_set_cb(idnode_t * in1,idnode_t * in2,void * origin)1310 access_entry_profile_set_cb ( idnode_t *in1, idnode_t *in2, void *origin )
1311 {
1312 access_entry_t *ae = (access_entry_t *)in1;
1313 idnode_list_mapping_t *ilm;
1314 profile_t *pro = (profile_t *)in2;
1315 ilm = idnode_list_link(in1, &ae->ae_profiles, in2, &pro->pro_accesses, origin, 1);
1316 return ilm ? 1 : 0;
1317 }
1318
1319 static int
access_entry_profile_set(void * o,const void * v)1320 access_entry_profile_set(void *o, const void *v)
1321 {
1322 access_entry_t *ae = (access_entry_t *)o;
1323 return idnode_list_set1(&ae->ae_id, &ae->ae_profiles,
1324 &profile_class, (htsmsg_t *)v,
1325 access_entry_profile_set_cb);
1326 }
1327
1328 static const void *
access_entry_profile_get(void * o)1329 access_entry_profile_get(void *o)
1330 {
1331 return idnode_list_get1(&((access_entry_t *)o)->ae_profiles);
1332 }
1333
1334 static char *
access_entry_profile_rend(void * o,const char * lang)1335 access_entry_profile_rend (void *o, const char *lang)
1336 {
1337 return idnode_list_get_csv1(&((access_entry_t *)o)->ae_profiles, lang);
1338 }
1339
1340 static htsmsg_t *
access_entry_conn_limit_type_enum(void * p,const char * lang)1341 access_entry_conn_limit_type_enum ( void *p, const char *lang )
1342 {
1343 static struct strtab
1344 conn_limit_type_tab[] = {
1345 { N_("All (Streaming plus DVR)"), ACCESS_CONN_LIMIT_TYPE_ALL },
1346 { N_("Streaming"), ACCESS_CONN_LIMIT_TYPE_STREAMING },
1347 { N_("DVR"), ACCESS_CONN_LIMIT_TYPE_DVR },
1348 };
1349 return strtab2htsmsg(conn_limit_type_tab, 1, lang);
1350 }
1351
1352 htsmsg_t *
language_get_list(void * obj,const char * lang)1353 language_get_list ( void *obj, const char *lang )
1354 {
1355 htsmsg_t *m = htsmsg_create_map();
1356 htsmsg_add_str(m, "type", "api");
1357 htsmsg_add_str(m, "uri", "language/locale");
1358 return m;
1359 }
1360
1361 htsmsg_t *
language_get_ui_list(void * obj,const char * lang)1362 language_get_ui_list ( void *obj, const char *lang )
1363 {
1364 htsmsg_t *m = htsmsg_create_map();
1365 htsmsg_add_str(m, "type", "api");
1366 htsmsg_add_str(m, "uri", "language/ui_locale");
1367 return m;
1368 }
1369
1370 htsmsg_t *
user_get_userlist(void * obj,const char * lang)1371 user_get_userlist ( void *obj, const char *lang )
1372 {
1373 htsmsg_t *m = htsmsg_create_map();
1374 htsmsg_add_str(m, "type", "api");
1375 htsmsg_add_str(m, "uri", "access/entry/userlist");
1376 htsmsg_add_str(m, "event", "access");
1377 return m;
1378 }
1379
1380 static htsmsg_t *
uilevel_get_list(void * o,const char * lang)1381 uilevel_get_list ( void *o, const char *lang )
1382 {
1383 static const struct strtab tab[] = {
1384 { N_("Default"), UILEVEL_DEFAULT },
1385 { N_("Basic"), UILEVEL_BASIC },
1386 { N_("Advanced"), UILEVEL_ADVANCED },
1387 { N_("Expert"), UILEVEL_EXPERT },
1388 };
1389 return strtab2htsmsg(tab, 1, lang);
1390 }
1391
1392 static htsmsg_t *
uilevel_nochange_get_list(void * o,const char * lang)1393 uilevel_nochange_get_list ( void *o, const char *lang )
1394 {
1395 static const struct strtab tab[] = {
1396 { N_("Default"), -1 },
1397 { N_("No"), 0 },
1398 { N_("Yes"), 1 },
1399 };
1400 return strtab2htsmsg(tab, 1, lang);
1401 }
1402
1403 htsmsg_t *
theme_get_ui_list(void * p,const char * lang)1404 theme_get_ui_list ( void *p, const char *lang )
1405 {
1406 static struct strtab_str tab[] = {
1407 { N_("Blue"), "blue" },
1408 { N_("Gray"), "gray" },
1409 { N_("Access"), "access" },
1410 };
1411 return strtab2htsmsg_str(tab, 1, lang);
1412 }
1413
1414 static idnode_slist_t access_entry_class_change_slist[] = {
1415 {
1416 .id = "change_rights",
1417 .name = N_("Rights"),
1418 .off = offsetof(access_entry_t, ae_change_rights),
1419 },
1420 {
1421 .id = "change_chrange",
1422 .name = N_("Channel number range"),
1423 .off = offsetof(access_entry_t, ae_change_chrange),
1424 },
1425 {
1426 .id = "change_chtags",
1427 .name = N_("Channel tags"),
1428 .off = offsetof(access_entry_t, ae_change_chtags),
1429 },
1430 {
1431 .id = "change_dvr_configs",
1432 .name = N_("DVR configurations"),
1433 .off = offsetof(access_entry_t, ae_change_dvr_configs),
1434 },
1435 {
1436 .id = "change_profiles",
1437 .name = N_("Streaming profiles"),
1438 .off = offsetof(access_entry_t, ae_change_profiles),
1439 },
1440 {
1441 .id = "change_conn_limit",
1442 .name = N_("Connection limits"),
1443 .off = offsetof(access_entry_t, ae_change_conn_limit),
1444 },
1445 {
1446 .id = "change_lang",
1447 .name = N_("Language"),
1448 .off = offsetof(access_entry_t, ae_change_lang),
1449 },
1450 {
1451 .id = "change_lang_ui",
1452 .name = N_("Web interface language"),
1453 .off = offsetof(access_entry_t, ae_change_lang_ui),
1454 },
1455 {
1456 .id = "change_theme",
1457 .name = N_("Theme"),
1458 .off = offsetof(access_entry_t, ae_change_theme),
1459 },
1460 {
1461 .id = "change_uilevel",
1462 .name = N_("User interface level"),
1463 .off = offsetof(access_entry_t, ae_change_uilevel),
1464 },
1465 {}
1466 };
1467
1468 static htsmsg_t *
access_entry_class_change_enum(void * obj,const char * lang)1469 access_entry_class_change_enum ( void *obj, const char *lang )
1470 {
1471 return idnode_slist_enum(obj, access_entry_class_change_slist, lang);
1472 }
1473
1474 static const void *
access_entry_class_change_get(void * obj)1475 access_entry_class_change_get ( void *obj )
1476 {
1477 return idnode_slist_get(obj, access_entry_class_change_slist);
1478 }
1479
1480 static char *
access_entry_class_change_rend(void * obj,const char * lang)1481 access_entry_class_change_rend ( void *obj, const char *lang )
1482 {
1483 return idnode_slist_rend(obj, access_entry_class_change_slist, lang);
1484 }
1485
1486 static int
access_entry_class_change_set(void * obj,const void * p)1487 access_entry_class_change_set ( void *obj, const void *p )
1488 {
1489 return idnode_slist_set(obj, access_entry_class_change_slist, p);
1490 }
1491
1492
1493 static idnode_slist_t access_entry_class_streaming_slist[] = {
1494 {
1495 .id = "basic",
1496 .name = N_("Basic"),
1497 .off = offsetof(access_entry_t, ae_streaming),
1498 },
1499 {
1500 .id = "advanced",
1501 .name = N_("Advanced"),
1502 .off = offsetof(access_entry_t, ae_adv_streaming),
1503 },
1504 {
1505 .id = "htsp",
1506 .name = N_("HTSP"),
1507 .off = offsetof(access_entry_t, ae_htsp_streaming),
1508 },
1509 {}
1510 };
1511
1512 static htsmsg_t *
access_entry_class_streaming_enum(void * obj,const char * lang)1513 access_entry_class_streaming_enum ( void *obj, const char *lang )
1514 {
1515 return idnode_slist_enum(obj, access_entry_class_streaming_slist, lang);
1516 }
1517
1518 static const void *
access_entry_class_streaming_get(void * obj)1519 access_entry_class_streaming_get ( void *obj )
1520 {
1521 return idnode_slist_get(obj, access_entry_class_streaming_slist);
1522 }
1523
1524 static char *
access_entry_class_streaming_rend(void * obj,const char * lang)1525 access_entry_class_streaming_rend ( void *obj, const char *lang )
1526 {
1527 return idnode_slist_rend(obj, access_entry_class_streaming_slist, lang);
1528 }
1529
1530 static int
access_entry_class_streaming_set(void * obj,const void * p)1531 access_entry_class_streaming_set ( void *obj, const void *p )
1532 {
1533 return idnode_slist_set(obj, access_entry_class_streaming_slist, p);
1534 }
1535
1536 static idnode_slist_t access_entry_class_dvr_slist[] = {
1537 {
1538 .id = "basic",
1539 .name = N_("Basic"),
1540 .off = offsetof(access_entry_t, ae_dvr),
1541 },
1542 {
1543 .id = "htsp",
1544 .name = N_("HTSP"),
1545 .off = offsetof(access_entry_t, ae_htsp_dvr),
1546 },
1547 {
1548 .id = "all",
1549 .name = N_("View all"),
1550 .off = offsetof(access_entry_t, ae_all_dvr),
1551 },
1552 {
1553 .id = "all_rw",
1554 .name = N_("Manage all"),
1555 .off = offsetof(access_entry_t, ae_all_rw_dvr),
1556 },
1557 {
1558 .id = "failed",
1559 .name = N_("Failed view"),
1560 .off = offsetof(access_entry_t, ae_failed_dvr),
1561 },
1562 {}
1563 };
1564
1565 static htsmsg_t *
access_entry_class_dvr_enum(void * obj,const char * lang)1566 access_entry_class_dvr_enum ( void *obj, const char *lang )
1567 {
1568 return idnode_slist_enum(obj, access_entry_class_dvr_slist, lang);
1569 }
1570
1571 static const void *
access_entry_class_dvr_get(void * obj)1572 access_entry_class_dvr_get ( void *obj )
1573 {
1574 return idnode_slist_get(obj, access_entry_class_dvr_slist);
1575 }
1576
1577 static char *
access_entry_class_dvr_rend(void * obj,const char * lang)1578 access_entry_class_dvr_rend ( void *obj, const char *lang )
1579 {
1580 return idnode_slist_rend(obj, access_entry_class_dvr_slist, lang);
1581 }
1582
1583 static int
access_entry_class_dvr_set(void * obj,const void * p)1584 access_entry_class_dvr_set ( void *obj, const void *p )
1585 {
1586 return idnode_slist_set(obj, access_entry_class_dvr_slist, p);
1587 }
1588
1589 CLASS_DOC(access_entry)
1590 PROP_DOC(viewlevel_access_entries)
1591 PROP_DOC(themes)
1592 PROP_DOC(connection_limit)
1593 PROP_DOC(persistent_viewlevel)
1594 PROP_DOC(streaming_profile)
1595 PROP_DOC(change_parameters)
1596
1597 const idclass_t access_entry_class = {
1598 .ic_class = "access",
1599 .ic_caption = N_("Users - Access Entries"),
1600 .ic_event = "access",
1601 .ic_perm_def = ACCESS_ADMIN,
1602 .ic_doc = tvh_doc_access_entry_class,
1603 .ic_save = access_entry_class_save,
1604 .ic_get_title = access_entry_class_get_title,
1605 .ic_delete = access_entry_class_delete,
1606 .ic_moveup = access_entry_class_moveup,
1607 .ic_movedown = access_entry_class_movedown,
1608 .ic_properties = (const property_t[]){
1609 {
1610 .type = PT_INT,
1611 .id = "index",
1612 .name = N_("Index"),
1613 .off = offsetof(access_entry_t, ae_index),
1614 .opts = PO_RDONLY | PO_HIDDEN | PO_NOUI,
1615 },
1616 {
1617 .type = PT_BOOL,
1618 .id = "enabled",
1619 .name = N_("Enabled"),
1620 .desc = N_("Enable/Disable the entry."),
1621 .def.i = 1,
1622 .off = offsetof(access_entry_t, ae_enabled),
1623 },
1624 {
1625 .type = PT_STR,
1626 .id = "username",
1627 .name = N_("Username"),
1628 .desc = N_("Username for the entry (login username)."),
1629 .off = offsetof(access_entry_t, ae_username),
1630 },
1631 {
1632 .type = PT_STR,
1633 .id = "prefix",
1634 .name = N_("Allowed networks"),
1635 .desc = N_("List of allowed IPv4 or IPv6 hosts or networks (comma-separated)."),
1636 .set = access_entry_class_prefix_set,
1637 .get = access_entry_class_prefix_get,
1638 .opts = PO_ADVANCED
1639 },
1640 {
1641 .type = PT_INT,
1642 .islist = 1,
1643 .id = "change",
1644 .name = N_("Change parameters"),
1645 .desc = N_("Specify the parameters to be changed. See Help for details."),
1646 .doc = prop_doc_change_parameters,
1647 .list = access_entry_class_change_enum,
1648 .get = access_entry_class_change_get,
1649 .set = access_entry_class_change_set,
1650 .rend = access_entry_class_change_rend,
1651 .opts = PO_DOC_NLIST,
1652 },
1653 {
1654 .type = PT_INT,
1655 .id = "uilevel",
1656 .name = N_("User interface level"),
1657 .desc = N_("Default user interface level."),
1658 .doc = prop_doc_viewlevel_access_entries,
1659 .off = offsetof(access_entry_t, ae_uilevel),
1660 .list = uilevel_get_list,
1661 .opts = PO_EXPERT | PO_DOC_NLIST,
1662 },
1663 {
1664 .type = PT_INT,
1665 .id = "uilevel_nochange",
1666 .name = N_("Persistent user interface level"),
1667 .desc = N_("Prevent the user from overriding the default user "
1668 "interface level setting and removes the view level "
1669 "drop-dowm from the interface."),
1670 .doc = prop_doc_persistent_viewlevel,
1671 .off = offsetof(access_entry_t, ae_uilevel_nochange),
1672 .list = uilevel_nochange_get_list,
1673 .opts = PO_EXPERT | PO_DOC_NLIST,
1674 },
1675 {
1676 .type = PT_STR,
1677 .id = "lang",
1678 .name = N_("Language"),
1679 .desc = N_("Default language."),
1680 .list = language_get_list,
1681 .off = offsetof(access_entry_t, ae_lang),
1682 .opts = PO_ADVANCED,
1683 },
1684 {
1685 .type = PT_STR,
1686 .id = "langui",
1687 .name = N_("Web interface language"),
1688 .desc = N_("Web interface language."),
1689 .list = language_get_ui_list,
1690 .off = offsetof(access_entry_t, ae_lang_ui),
1691 .opts = PO_ADVANCED,
1692 },
1693 {
1694 .type = PT_STR,
1695 .id = "themeui",
1696 .name = N_("Web theme"),
1697 .desc = N_("Web interface theme."),
1698 .doc = prop_doc_themes,
1699 .list = theme_get_ui_list,
1700 .off = offsetof(access_entry_t, ae_theme),
1701 .opts = PO_DOC_NLIST | PO_ADVANCED,
1702 },
1703 {
1704 .type = PT_INT,
1705 .islist = 1,
1706 .id = "streaming",
1707 .name = N_("Streaming"),
1708 .desc = N_("Streaming flags, allow/disallow HTTP streaming, "
1709 "advanced HTTP streaming (e.g, direct service or mux links), "
1710 "HTSP protocol streaming (e.g, Kodi (via pvr.hts) or Movian."),
1711 .list = access_entry_class_streaming_enum,
1712 .get = access_entry_class_streaming_get,
1713 .set = access_entry_class_streaming_set,
1714 .rend = access_entry_class_streaming_rend,
1715 .opts = PO_DOC_NLIST,
1716 },
1717 {
1718 .type = PT_STR,
1719 .islist = 1,
1720 .id = "profile",
1721 .name = N_("Streaming profiles"),
1722 .desc = N_("The streaming profile to use/used. If not set, "
1723 "the default will be used."),
1724 .doc = prop_doc_streaming_profile,
1725 .set = access_entry_profile_set,
1726 .get = access_entry_profile_get,
1727 .list = profile_class_get_list,
1728 .rend = access_entry_profile_rend,
1729 .opts = PO_ADVANCED,
1730 },
1731 {
1732 .type = PT_INT,
1733 .islist = 1,
1734 .id = "dvr",
1735 .name = N_("Video recorder"),
1736 .desc = N_("Video recorder flags, allow/disallow access to video recorder "
1737 "functionality (including Autorecs), allow/disallow users to "
1738 "view other DVR entries, allow/disallow users to work with "
1739 "DVR entries of other users (remove, edit) etc."),
1740 .list = access_entry_class_dvr_enum,
1741 .get = access_entry_class_dvr_get,
1742 .set = access_entry_class_dvr_set,
1743 .rend = access_entry_class_dvr_rend,
1744 .opts = PO_DOC_NLIST,
1745 },
1746 {
1747 .type = PT_BOOL,
1748 .id = "htsp_anonymize",
1749 .name = N_("Anonymize HTSP access"),
1750 .desc = N_("Do not send any stream specific information to "
1751 "the HTSP client like signal strength, input source "
1752 "etc."),
1753 .off = offsetof(access_entry_t, ae_htsp_anonymize),
1754 .opts = PO_EXPERT | PO_HIDDEN,
1755 },
1756 {
1757 .type = PT_STR,
1758 .islist = 1,
1759 .id = "dvr_config",
1760 .name = N_("DVR configuration profiles"),
1761 .desc = N_("Allowed DVR profiles. This limits the profiles "
1762 "the user has access to."),
1763 .set = access_entry_dvr_config_set,
1764 .get = access_entry_dvr_config_get,
1765 .list = dvr_entry_class_config_name_list,
1766 .rend = access_entry_dvr_config_rend,
1767 .opts = PO_ADVANCED,
1768 },
1769 {
1770 .type = PT_BOOL,
1771 .id = "webui",
1772 .name = N_("Web interface"),
1773 .desc = N_("Allow/Disallow web interface access (this "
1774 " includes access to the EPG)."),
1775 .off = offsetof(access_entry_t, ae_webui),
1776 },
1777 {
1778 .type = PT_BOOL,
1779 .id = "admin",
1780 .name = N_("Admin"),
1781 .desc = N_("Allow/Disallow access to the 'Configuration' tab."),
1782 .off = offsetof(access_entry_t, ae_admin),
1783 },
1784 {
1785 .type = PT_INT,
1786 .id = "conn_limit_type",
1787 .name = N_("Connection limit type"),
1788 .desc = N_("Restrict connections to this type."),
1789 .doc = prop_doc_connection_limit,
1790 .off = offsetof(access_entry_t, ae_conn_limit_type),
1791 .list = access_entry_conn_limit_type_enum,
1792 .opts = PO_EXPERT | PO_DOC_NLIST,
1793 },
1794 {
1795 .type = PT_U32,
1796 .id = "conn_limit",
1797 .name = N_("Limit connections"),
1798 .desc = N_("The number of allowed connections this user can "
1799 "make to the server."),
1800 .off = offsetof(access_entry_t, ae_conn_limit),
1801 .opts = PO_EXPERT
1802 },
1803 {
1804 .type = PT_S64,
1805 .intextra = CHANNEL_SPLIT,
1806 .id = "channel_min",
1807 .name = N_("Minimal channel number"),
1808 .desc = N_("Lowest channel number the user can access."),
1809 .off = offsetof(access_entry_t, ae_chmin),
1810 .opts = PO_ADVANCED,
1811 },
1812 {
1813 .type = PT_S64,
1814 .intextra = CHANNEL_SPLIT,
1815 .id = "channel_max",
1816 .name = N_("Maximal channel number"),
1817 .desc = N_("Highest channel number the user can access."),
1818 .off = offsetof(access_entry_t, ae_chmax),
1819 .opts = PO_ADVANCED,
1820 },
1821 {
1822 .type = PT_BOOL,
1823 .id = "channel_tag_exclude",
1824 .name = N_("Exclude channel tags"),
1825 .desc = N_("Enable exclusion of user-config defined channel "
1826 "tags. This will prevent the user from accessing "
1827 "channels associated with the tags selected (below)."),
1828 .off = offsetof(access_entry_t, ae_chtags_exclude),
1829 .opts = PO_ADVANCED,
1830 },
1831 {
1832 .type = PT_STR,
1833 .islist = 1,
1834 .id = "channel_tag",
1835 .name = N_("Channel tags"),
1836 .desc = N_("Channel tags the user is allowed access to/excluded from."),
1837 .set = access_entry_chtag_set,
1838 .get = access_entry_chtag_get,
1839 .list = channel_tag_class_get_list,
1840 .rend = access_entry_chtag_rend,
1841 .opts = PO_ADVANCED,
1842 },
1843 {
1844 .type = PT_STR,
1845 .id = "comment",
1846 .name = N_("Comment"),
1847 .desc = N_("Free-form text field, enter whatever you like here."),
1848 .off = offsetof(access_entry_t, ae_comment),
1849 },
1850 {
1851 .type = PT_BOOL,
1852 .id = "wizard",
1853 .name = N_("Wizard"),
1854 .off = offsetof(access_entry_t, ae_wizard),
1855 .opts = PO_NOUI
1856 },
1857 {}
1858 }
1859 };
1860
1861 /*
1862 * Password table
1863 */
1864
1865 static int passwd_entry_class_password_set(void *o, const void *v);
1866
1867 static int
passwd_verify2(const char * username,verify_callback_t verify,void * aux,const char * username2,const char * passwd2)1868 passwd_verify2
1869 (const char *username, verify_callback_t verify, void *aux,
1870 const char *username2, const char *passwd2)
1871 {
1872 if (username == NULL || username[0] == '\0' ||
1873 username2 == NULL || username2[0] == '\0' ||
1874 passwd2 == NULL)
1875 return -1;
1876
1877 if (strcmp(username, username2))
1878 return -1;
1879
1880 return verify(aux, passwd2) ? 0 : -1;
1881 }
1882
1883 static int
passwd_verify(const char * username,verify_callback_t verify,void * aux)1884 passwd_verify
1885 (const char *username, verify_callback_t verify, void *aux)
1886 {
1887 passwd_entry_t *pw;
1888
1889 TAILQ_FOREACH(pw, &passwd_entries, pw_link)
1890 if (pw->pw_enabled &&
1891 !passwd_verify2(username, verify, aux,
1892 pw->pw_username, pw->pw_password))
1893 return 0;
1894 return -1;
1895 }
1896
1897 passwd_entry_t *
passwd_entry_create(const char * uuid,htsmsg_t * conf)1898 passwd_entry_create(const char *uuid, htsmsg_t *conf)
1899 {
1900 passwd_entry_t *pw;
1901 const char *s;
1902
1903 lock_assert(&global_lock);
1904
1905 pw = calloc(1, sizeof(passwd_entry_t));
1906
1907 if (idnode_insert(&pw->pw_id, uuid, &passwd_entry_class, 0)) {
1908 if (uuid)
1909 tvherror(LS_ACCESS, "invalid uuid '%s'", uuid);
1910 free(pw);
1911 return NULL;
1912 }
1913
1914 if (conf) {
1915 pw->pw_enabled = 1;
1916 idnode_load(&pw->pw_id, conf);
1917 /* note password has PO_NOSAVE, thus it must be set manually */
1918 if ((s = htsmsg_get_str(conf, "password")) != NULL)
1919 passwd_entry_class_password_set(pw, s);
1920 }
1921
1922 TAILQ_INSERT_TAIL(&passwd_entries, pw, pw_link);
1923
1924 return pw;
1925 }
1926
1927 void
passwd_entry_destroy(passwd_entry_t * pw,int delconf)1928 passwd_entry_destroy(passwd_entry_t *pw, int delconf)
1929 {
1930 char ubuf[UUID_HEX_SIZE];
1931
1932 if (pw == NULL)
1933 return;
1934
1935 idnode_save_check(&pw->pw_id, delconf);
1936
1937 if (delconf)
1938 hts_settings_remove("passwd/%s", idnode_uuid_as_str(&pw->pw_id, ubuf));
1939 TAILQ_REMOVE(&passwd_entries, pw, pw_link);
1940 idnode_unlink(&pw->pw_id);
1941 free(pw->pw_username);
1942 free(pw->pw_password);
1943 free(pw->pw_password2);
1944 free(pw->pw_comment);
1945 free(pw);
1946 }
1947
1948 static htsmsg_t *
passwd_entry_class_save(idnode_t * self,char * filename,size_t fsize)1949 passwd_entry_class_save(idnode_t *self, char *filename, size_t fsize)
1950 {
1951 passwd_entry_t *pw = (passwd_entry_t *)self;
1952 char ubuf[UUID_HEX_SIZE];
1953 htsmsg_t *c = htsmsg_create_map();
1954 idnode_save(&pw->pw_id, c);
1955 snprintf(filename, fsize, "passwd/%s", idnode_uuid_as_str(&pw->pw_id, ubuf));
1956 return c;
1957 }
1958
1959 static void
passwd_entry_class_delete(idnode_t * self)1960 passwd_entry_class_delete(idnode_t *self)
1961 {
1962 passwd_entry_t *pw = (passwd_entry_t *)self;
1963 passwd_entry_destroy(pw, 1);
1964 }
1965
1966 static const char *
passwd_entry_class_get_title(idnode_t * self,const char * lang)1967 passwd_entry_class_get_title (idnode_t *self, const char *lang)
1968 {
1969 passwd_entry_t *pw = (passwd_entry_t *)self;
1970
1971 if (pw->pw_comment && pw->pw_comment[0] != '\0')
1972 return pw->pw_comment;
1973 return pw->pw_username ?: "";
1974 }
1975
1976 static int
passwd_entry_class_password_set(void * o,const void * v)1977 passwd_entry_class_password_set(void *o, const void *v)
1978 {
1979 passwd_entry_t *pw = (passwd_entry_t *)o;
1980 char buf[256], result[300];
1981
1982 if (strcmp(v ?: "", pw->pw_password ?: "")) {
1983 snprintf(buf, sizeof(buf), "TVHeadend-Hide-%s", (const char *)v ?: "");
1984 base64_encode(result, sizeof(result), (uint8_t *)buf, strlen(buf));
1985 free(pw->pw_password2);
1986 pw->pw_password2 = strdup(result);
1987 free(pw->pw_password);
1988 pw->pw_password = strdup((const char *)v ?: "");
1989 return 1;
1990 }
1991 return 0;
1992 }
1993
1994 static int
passwd_entry_class_password2_set(void * o,const void * v)1995 passwd_entry_class_password2_set(void *o, const void *v)
1996 {
1997 passwd_entry_t *pw = (passwd_entry_t *)o;
1998 char result[300];
1999 int l;
2000
2001 if (strcmp(v ?: "", pw->pw_password2 ?: "")) {
2002 if (v && ((const char *)v)[0] != '\0') {
2003 l = base64_decode((uint8_t *)result, v, sizeof(result)-1);
2004 if (l < 0)
2005 l = 0;
2006 result[l] = '\0';
2007 free(pw->pw_password);
2008 pw->pw_password = strdup(result + 15);
2009 free(pw->pw_password2);
2010 pw->pw_password2 = strdup((const char *)v);
2011 return 1;
2012 }
2013 }
2014 return 0;
2015 }
2016
2017 CLASS_DOC(passwd)
2018
2019 const idclass_t passwd_entry_class = {
2020 .ic_class = "passwd",
2021 .ic_caption = N_("Users - Passwords"),
2022 .ic_event = "passwd",
2023 .ic_perm_def = ACCESS_ADMIN,
2024 .ic_doc = tvh_doc_passwd_class,
2025 .ic_save = passwd_entry_class_save,
2026 .ic_get_title = passwd_entry_class_get_title,
2027 .ic_delete = passwd_entry_class_delete,
2028 .ic_properties = (const property_t[]){
2029 {
2030 .type = PT_BOOL,
2031 .id = "enabled",
2032 .name = N_("Enabled"),
2033 .desc = N_("Enable/disable the entry."),
2034 .def.i = 1,
2035 .off = offsetof(passwd_entry_t, pw_enabled),
2036 },
2037 {
2038 .type = PT_STR,
2039 .id = "username",
2040 .name = N_("Username"),
2041 .desc = N_("Username of the entry (this should match a "
2042 "username from within the \"Access Entries\" tab)."),
2043 .off = offsetof(passwd_entry_t, pw_username),
2044 },
2045 {
2046 .type = PT_STR,
2047 .id = "password",
2048 .name = N_("Password"),
2049 .desc = N_("Password for the entry."),
2050 .off = offsetof(passwd_entry_t, pw_password),
2051 .opts = PO_PASSWORD | PO_NOSAVE,
2052 .set = passwd_entry_class_password_set,
2053 },
2054 {
2055 .type = PT_STR,
2056 .id = "password2",
2057 .name = N_("Password2"),
2058 .off = offsetof(passwd_entry_t, pw_password2),
2059 .opts = PO_PASSWORD | PO_HIDDEN | PO_EXPERT | PO_WRONCE | PO_NOUI,
2060 .set = passwd_entry_class_password2_set,
2061 },
2062 {
2063 .type = PT_STR,
2064 .id = "comment",
2065 .name = N_("Comment"),
2066 .desc = N_("Free-form text field, enter whatever you like here."),
2067 .off = offsetof(passwd_entry_t, pw_comment),
2068 },
2069 {
2070 .type = PT_BOOL,
2071 .id = "wizard",
2072 .name = N_("Wizard"),
2073 .off = offsetof(passwd_entry_t, pw_wizard),
2074 .opts = PO_NOUI
2075 },
2076 {}
2077 }
2078 };
2079
2080 /**
2081 * IP block list
2082 */
2083
2084 ipblock_entry_t *
ipblock_entry_create(const char * uuid,htsmsg_t * conf)2085 ipblock_entry_create(const char *uuid, htsmsg_t *conf)
2086 {
2087 ipblock_entry_t *ib;
2088
2089 lock_assert(&global_lock);
2090
2091 ib = calloc(1, sizeof(ipblock_entry_t));
2092
2093 TAILQ_INIT(&ib->ib_ipmasks);
2094
2095 if (idnode_insert(&ib->ib_id, uuid, &ipblock_entry_class, 0)) {
2096 if (uuid)
2097 tvherror(LS_ACCESS, "invalid uuid '%s'", uuid);
2098 free(ib);
2099 return NULL;
2100 }
2101
2102 if (conf) {
2103 ib->ib_enabled = 1;
2104 idnode_load(&ib->ib_id, conf);
2105 }
2106
2107 TAILQ_INSERT_TAIL(&ipblock_entries, ib, ib_link);
2108
2109 return ib;
2110 }
2111
2112 static void
ipblock_entry_destroy(ipblock_entry_t * ib,int delconf)2113 ipblock_entry_destroy(ipblock_entry_t *ib, int delconf)
2114 {
2115 if (ib == NULL)
2116 return;
2117 idnode_save_check(&ib->ib_id, delconf);
2118 TAILQ_REMOVE(&ipblock_entries, ib, ib_link);
2119 idnode_unlink(&ib->ib_id);
2120 free(ib->ib_comment);
2121 free(ib);
2122 }
2123
2124 static htsmsg_t *
ipblock_entry_class_save(idnode_t * self,char * filename,size_t fsize)2125 ipblock_entry_class_save(idnode_t *self, char *filename, size_t fsize)
2126 {
2127 ipblock_entry_t *ib = (ipblock_entry_t *)self;
2128 htsmsg_t *c = htsmsg_create_map();
2129 char ubuf[UUID_HEX_SIZE];
2130 idnode_save(&ib->ib_id, c);
2131 snprintf(filename, fsize, "ipblock/%s", idnode_uuid_as_str(&ib->ib_id, ubuf));
2132 return c;
2133 }
2134
2135 static const char *
ipblock_entry_class_get_title(idnode_t * self,const char * lang)2136 ipblock_entry_class_get_title (idnode_t *self, const char *lang)
2137 {
2138 ipblock_entry_t *ib = (ipblock_entry_t *)self;
2139
2140 if (ib->ib_comment && ib->ib_comment[0] != '\0')
2141 return ib->ib_comment;
2142 return N_("IP blocking");
2143 }
2144
2145 static void
ipblock_entry_class_delete(idnode_t * self)2146 ipblock_entry_class_delete(idnode_t *self)
2147 {
2148 ipblock_entry_t *ib = (ipblock_entry_t *)self;
2149 char ubuf[UUID_HEX_SIZE];
2150
2151 hts_settings_remove("ipblock/%s", idnode_uuid_as_str(&ib->ib_id, ubuf));
2152 ipblock_entry_destroy(ib, 1);
2153 }
2154
2155 static int
ipblock_entry_class_prefix_set(void * o,const void * v)2156 ipblock_entry_class_prefix_set(void *o, const void *v)
2157 {
2158 access_set_prefix(&((ipblock_entry_t *)o)->ib_ipmasks, (const char *)v, 0);
2159 return 1;
2160 }
2161
2162 static const void *
ipblock_entry_class_prefix_get(void * o)2163 ipblock_entry_class_prefix_get(void *o)
2164 {
2165 prop_ptr = access_get_prefix(&((ipblock_entry_t *)o)->ib_ipmasks);
2166 return &prop_ptr;
2167 }
2168
2169 CLASS_DOC(ipblocking)
2170
2171 const idclass_t ipblock_entry_class = {
2172 .ic_class = "ipblocking",
2173 .ic_caption = N_("Users - IP Blocking"),
2174 .ic_event = "ipblocking",
2175 .ic_perm_def = ACCESS_ADMIN,
2176 .ic_doc = tvh_doc_ipblocking_class,
2177 .ic_save = ipblock_entry_class_save,
2178 .ic_get_title = ipblock_entry_class_get_title,
2179 .ic_delete = ipblock_entry_class_delete,
2180 .ic_properties = (const property_t[]){
2181 {
2182 .type = PT_BOOL,
2183 .id = "enabled",
2184 .name = N_("Enabled"),
2185 .desc = N_("Enable/disable the entry."),
2186 .off = offsetof(ipblock_entry_t, ib_enabled),
2187 },
2188 {
2189 .type = PT_STR,
2190 .id = "prefix",
2191 .name = N_("Network prefix"),
2192 .desc = N_("The network prefix(es) to block, "
2193 "e.g.192.168.2.0/24 (comma-separated list)."),
2194 .set = ipblock_entry_class_prefix_set,
2195 .get = ipblock_entry_class_prefix_get,
2196 },
2197 {
2198 .type = PT_STR,
2199 .id = "comment",
2200 .desc = N_("Free-form text field, enter whatever you like here."),
2201 .name = N_("Comment"),
2202 .off = offsetof(ipblock_entry_t, ib_comment),
2203 },
2204 {}
2205 }
2206 };
2207
2208 /**
2209 *
2210 */
2211 void
access_init(int createdefault,int noacl)2212 access_init(int createdefault, int noacl)
2213 {
2214 htsmsg_t *c, *m;
2215 htsmsg_field_t *f;
2216 access_entry_t *ae;
2217 const char *s;
2218
2219 access_noacl = noacl;
2220 if (noacl)
2221 tvhwarn(LS_ACCESS, "Access control checking disabled");
2222
2223 TAILQ_INIT(&access_entries);
2224 TAILQ_INIT(&access_tickets);
2225 TAILQ_INIT(&passwd_entries);
2226 TAILQ_INIT(&ipblock_entries);
2227
2228 idclass_register(&access_entry_class);
2229 idclass_register(&passwd_entry_class);
2230 idclass_register(&ipblock_entry_class);
2231
2232 /* Load ipblock entries */
2233 if ((c = hts_settings_load("ipblock")) != NULL) {
2234 HTSMSG_FOREACH(f, c) {
2235 if (!(m = htsmsg_field_get_map(f))) continue;
2236 (void)ipblock_entry_create(f->hmf_name, m);
2237 }
2238 htsmsg_destroy(c);
2239 }
2240
2241 /* Load passwd entries */
2242 if ((c = hts_settings_load("passwd")) != NULL) {
2243 HTSMSG_FOREACH(f, c) {
2244 if (!(m = htsmsg_field_get_map(f))) continue;
2245 (void)passwd_entry_create(f->hmf_name, m);
2246 }
2247 htsmsg_destroy(c);
2248 }
2249
2250 /* Load ACL entries */
2251 if ((c = hts_settings_load("accesscontrol")) != NULL) {
2252 HTSMSG_FOREACH(f, c) {
2253 if (!(m = htsmsg_field_get_map(f))) continue;
2254 (void)access_entry_create(f->hmf_name, m);
2255 }
2256 htsmsg_destroy(c);
2257 access_entry_reindex();
2258 }
2259
2260 if(createdefault && TAILQ_FIRST(&access_entries) == NULL) {
2261 /* No records available */
2262 ae = access_entry_create(NULL, NULL);
2263
2264 free(ae->ae_comment);
2265 ae->ae_comment = strdup(ACCESS_DEFAULT_COMMENT);
2266
2267 ae->ae_enabled = 1;
2268 ae->ae_change_rights = 1;
2269 ae->ae_streaming = 1;
2270 ae->ae_adv_streaming = 1;
2271 ae->ae_htsp_streaming = 1;
2272 ae->ae_webui = 1;
2273 ae->ae_dvr = 1;
2274 ae->ae_htsp_dvr = 1;
2275 ae->ae_all_dvr = 1;
2276 ae->ae_all_rw_dvr = 1;
2277 ae->ae_failed_dvr = 1;
2278 ae->ae_admin = 1;
2279 access_entry_update_rights(ae);
2280
2281 idnode_changed(&ae->ae_id);
2282
2283 tvhwarn(LS_ACCESS, "Created default wide open access controle entry");
2284 }
2285
2286 if(!TAILQ_FIRST(&access_entries) && !noacl)
2287 tvherror(LS_ACCESS, "No access entries loaded");
2288
2289 /* Load superuser account */
2290
2291 if((m = hts_settings_load("superuser")) != NULL) {
2292 s = htsmsg_get_str(m, "username");
2293 superuser_username = s ? strdup(s) : NULL;
2294 s = htsmsg_get_str(m, "password");
2295 superuser_password = s ? strdup(s) : NULL;
2296 htsmsg_destroy(m);
2297 }
2298 }
2299
2300 void
access_done(void)2301 access_done(void)
2302 {
2303 access_entry_t *ae;
2304 access_ticket_t *at;
2305 passwd_entry_t *pw;
2306 ipblock_entry_t *ib;
2307
2308 pthread_mutex_lock(&global_lock);
2309 while ((ae = TAILQ_FIRST(&access_entries)) != NULL)
2310 access_entry_destroy(ae, 0);
2311 while ((at = TAILQ_FIRST(&access_tickets)) != NULL)
2312 access_ticket_destroy(at);
2313 while ((pw = TAILQ_FIRST(&passwd_entries)) != NULL)
2314 passwd_entry_destroy(pw, 0);
2315 while ((ib = TAILQ_FIRST(&ipblock_entries)) != NULL)
2316 ipblock_entry_destroy(ib, 0);
2317 free((void *)superuser_username);
2318 superuser_username = NULL;
2319 free((void *)superuser_password);
2320 superuser_password = NULL;
2321 pthread_mutex_unlock(&global_lock);
2322 }
2323