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