1 /*
2  * ProFTPD - FTP server daemon
3  * Copyright (c) 2008-2017 The ProFTPD Project team
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 2 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, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18  *
19  * As a special exemption, Public Flood Software/MacGyver aka Habeeb J. Dihu
20  * and other respective copyright holders give permission to link this program
21  * with OpenSSL, and distribute the resulting executable, without including
22  * the source code for OpenSSL in the source distribution.
23  */
24 
25 /* String manipulation functions. */
26 
27 #include "conf.h"
28 
29 /* Maximum number of matches that we will do in a given string. */
30 #define PR_STR_MAX_MATCHES			128
31 
str_vreplace(pool * p,unsigned int max_replaces,const char * s,va_list args)32 static const char *str_vreplace(pool *p, unsigned int max_replaces,
33     const char *s, va_list args) {
34   const char *src;
35   char *m, *r, *cp;
36   char *matches[PR_STR_MAX_MATCHES+1], *replaces[PR_STR_MAX_MATCHES+1];
37   char buf[PR_TUNABLE_PATH_MAX] = {'\0'}, *pbuf = NULL;
38   size_t nmatches = 0, rlen = 0;
39   int blen = 0;
40 
41   src = s;
42   cp = buf;
43   *cp = '\0';
44 
45   memset(matches, 0, sizeof(matches));
46   memset(replaces, 0, sizeof(replaces));
47 
48   blen = strlen(src) + 1;
49 
50   while ((m = va_arg(args, char *)) != NULL &&
51          nmatches < PR_STR_MAX_MATCHES) {
52     char *tmp = NULL;
53     unsigned int count = 0;
54 
55     r = va_arg(args, char *);
56     if (r == NULL) {
57       break;
58     }
59 
60     /* Increase the length of the needed buffer by the difference between
61      * the given match and replacement strings, multiplied by the number
62      * of times the match string occurs in the source string.
63      */
64     tmp = strstr(s, m);
65     while (tmp) {
66       pr_signals_handle();
67       count++;
68       if (count > max_replaces) {
69         errno = E2BIG;
70         return NULL;
71       }
72 
73       /* Be sure to increment the pointer returned by strstr(3), to
74        * advance past the beginning of the substring for which we are
75        * looking.  Otherwise, we just loop endlessly, seeing the same
76        * value for tmp over and over.
77        */
78       tmp += strlen(m);
79       tmp = strstr(tmp, m);
80     }
81 
82     /* We are only concerned about match/replacement strings that actually
83      * occur in the given string.
84      */
85     if (count) {
86       blen += count * (strlen(r) - strlen(m));
87       if (blen < 0) {
88         /* Integer overflow. In order to overflow this, somebody must be
89          * doing something very strange. The possibility still exists that
90          * we might not catch this overflow in extreme corner cases, but
91          * massive amounts of data (gigabytes) would need to be in s to
92          * trigger this, easily larger than any buffer we might use.
93          */
94         return s;
95       }
96       matches[nmatches] = m;
97       replaces[nmatches++] = r;
98     }
99   }
100 
101   /* If there are no matches, then there is nothing to replace. */
102   if (nmatches == 0) {
103     return s;
104   }
105 
106   /* Try to handle large buffer situations (i.e. escaping of PR_TUNABLE_PATH_MAX
107    * (>2048) correctly, but do not allow very big buffer sizes, that may
108    * be dangerous (BUFSIZ may be defined in stdio.h) in some library
109    * functions.
110    */
111 #ifndef BUFSIZ
112 # define BUFSIZ 8192
113 #endif
114 
115   if (blen >= BUFSIZ) {
116     errno = ENOSPC;
117     return NULL;
118   }
119 
120   cp = pbuf = (char *) pcalloc(p, ++blen);
121 
122   while (*src) {
123     char **mptr, **rptr;
124 
125     for (mptr = matches, rptr = replaces; *mptr; mptr++, rptr++) {
126       size_t mlen;
127 
128       mlen = strlen(*mptr);
129       rlen = strlen(*rptr);
130 
131       if (strncmp(src, *mptr, mlen) == 0) {
132         sstrncpy(cp, *rptr, blen - strlen(pbuf));
133 
134         if (((cp + rlen) - pbuf + 1) > blen) {
135           pr_log_pri(PR_LOG_ERR,
136             "WARNING: attempt to overflow internal ProFTPD buffers");
137           cp = pbuf;
138 
139           cp += (blen - 1);
140           goto done;
141 
142         } else {
143           cp += rlen;
144         }
145 
146         src += mlen;
147         break;
148       }
149     }
150 
151     if (!*mptr) {
152       if ((cp - pbuf + 1) >= blen) {
153         pr_log_pri(PR_LOG_ERR,
154           "WARNING: attempt to overflow internal ProFTPD buffers");
155         cp = pbuf;
156 
157         cp += (blen - 1);
158         goto done;
159       }
160 
161       *cp++ = *src++;
162     }
163   }
164 
165  done:
166   *cp = '\0';
167 
168   return pbuf;
169 }
170 
pr_str_quote(pool * p,const char * str)171 const char *pr_str_quote(pool *p, const char *str) {
172   if (p == NULL ||
173       str == NULL) {
174     errno = EINVAL;
175     return NULL;
176   }
177 
178   return sreplace(p, str, "\"", "\"\"", NULL);
179 }
180 
quote_dir(pool * p,char * path)181 const char *quote_dir(pool *p, char *path) {
182   return pr_str_quote(p, path);
183 }
184 
pr_str_replace(pool * p,unsigned int max_replaces,const char * s,...)185 const char *pr_str_replace(pool *p, unsigned int max_replaces,
186     const char *s, ...) {
187   va_list args;
188   const char *res = NULL;
189 
190   if (p == NULL ||
191       s == NULL ||
192       max_replaces == 0) {
193     errno = EINVAL;
194     return NULL;
195   }
196 
197   va_start(args, s);
198   res = str_vreplace(p, max_replaces, s, args);
199   va_end(args);
200 
201   return res;
202 }
203 
sreplace(pool * p,const char * s,...)204 const char *sreplace(pool *p, const char *s, ...) {
205   va_list args;
206   const char *res = NULL;
207 
208   if (p == NULL ||
209       s == NULL) {
210     errno = EINVAL;
211     return NULL;
212   }
213 
214   va_start(args, s);
215   res = str_vreplace(p, PR_STR_MAX_REPLACEMENTS, s, args);
216   va_end(args);
217 
218   if (res == NULL &&
219       errno == E2BIG) {
220     /* For backward compatible behavior. */
221     return s;
222   }
223 
224   return res;
225 }
226 
227 /* "safe" strcat, saves room for NUL at end of dst, and refuses to copy more
228  * than "n" bytes.
229  */
sstrcat(char * dst,const char * src,size_t n)230 char *sstrcat(char *dst, const char *src, size_t n) {
231   register char *d = dst;
232 
233   if (dst == NULL ||
234       src == NULL ||
235       n == 0) {
236     errno = EINVAL;
237     return NULL;
238   }
239 
240   /* Edge case short ciruit; strlcat(3) doesn't do what I think it should
241    * do for this particular case.
242    */
243   if (n > 1) {
244 #ifdef HAVE_STRLCAT
245     strlcat(dst, src, n);
246 
247 #else
248     for (; *d && n > 1; d++, n--) ;
249 
250     while (n-- > 1 && *src) {
251       *d++ = *src++;
252     }
253 
254     *d = '\0';
255 #endif /* HAVE_STRLCAT */
256 
257   } else {
258     *d = '\0';
259   }
260 
261   return dst;
262 }
263 
pstrdup(pool * p,const char * str)264 char *pstrdup(pool *p, const char *str) {
265   char *res;
266   size_t len;
267 
268   if (p == NULL ||
269       str == NULL) {
270     errno = EINVAL;
271     return NULL;
272   }
273 
274   len = strlen(str) + 1;
275 
276   res = palloc(p, len);
277   if (res != NULL) {
278     sstrncpy(res, str, len);
279   }
280 
281   return res;
282 }
283 
pstrndup(pool * p,const char * str,size_t n)284 char *pstrndup(pool *p, const char *str, size_t n) {
285   char *res;
286 
287   if (!p || !str) {
288     errno = EINVAL;
289     return NULL;
290   }
291 
292   res = palloc(p, n + 1);
293   sstrncpy(res, str, n + 1);
294   return res;
295 }
296 
pdircat(pool * p,...)297 char *pdircat(pool *p, ...) {
298   char *argp, *ptr, *res;
299   char last;
300   int count = 0;
301   size_t len = 0, res_len = 0;
302   va_list ap;
303 
304   if (p == NULL) {
305     errno = EINVAL;
306     return NULL;
307   }
308 
309   va_start(ap, p);
310 
311   last = 0;
312 
313   while ((res = va_arg(ap, char *)) != NULL) {
314     /* If the first argument is "", we have to account for a leading /
315      * which must be added.
316      */
317     if (!count++ && !*res) {
318       len++;
319 
320     } else if (last && last != '/' && *res != '/') {
321       len++;
322 
323     } else if (last && last == '/' && *res == '/') {
324       len--;
325     }
326 
327     res_len = strlen(res);
328     len += res_len;
329     last = (*res ? res[res_len-1] : 0);
330   }
331 
332   va_end(ap);
333   ptr = res = (char *) pcalloc(p, len + 1);
334 
335   va_start(ap, p);
336 
337   last = res_len = 0;
338 
339   while ((argp = va_arg(ap, char *)) != NULL) {
340     size_t arglen;
341 
342     if (last && last == '/' && *argp == '/') {
343       argp++;
344 
345     } else if (last && last != '/' && *argp != '/') {
346       sstrcat(ptr, "/", len + 1);
347       ptr += 1;
348       res_len += 1;
349     }
350 
351     arglen = strlen(argp);
352     sstrcat(ptr, argp, len + 1);
353     ptr += arglen;
354     res_len += arglen;
355 
356     last = (*res ? res[res_len-1] : 0);
357   }
358 
359   va_end(ap);
360 
361   return res;
362 }
363 
pstrcat(pool * p,...)364 char *pstrcat(pool *p, ...) {
365   char *argp, *ptr, *res;
366   size_t len = 0;
367   va_list ap;
368 
369   if (p == NULL) {
370     errno = EINVAL;
371     return NULL;
372   }
373 
374   va_start(ap, p);
375 
376   while ((res = va_arg(ap, char *)) != NULL) {
377     len += strlen(res);
378   }
379 
380   va_end(ap);
381 
382   ptr = res = pcalloc(p, len + 1);
383 
384   va_start(ap, p);
385 
386   while ((argp = va_arg(ap, char *)) != NULL) {
387     size_t arglen;
388 
389     arglen = strlen(argp);
390     sstrcat(ptr, argp, len + 1);
391     ptr += arglen;
392   }
393 
394   va_end(ap);
395 
396   return res;
397 }
398 
pr_strnrstr(const char * s,size_t slen,const char * suffix,size_t suffixlen,int flags)399 int pr_strnrstr(const char *s, size_t slen, const char *suffix,
400     size_t suffixlen, int flags) {
401   int res = FALSE;
402 
403   if (s == NULL ||
404       suffix == NULL) {
405     errno = EINVAL;
406     return -1;
407   }
408 
409   if (slen == 0) {
410     slen = strlen(s);
411   }
412 
413   if (suffixlen == 0) {
414     suffixlen = strlen(suffix);
415   }
416 
417   if (slen == 0 &&
418       suffixlen == 0) {
419     return TRUE;
420   }
421 
422   if (slen == 0 ||
423       suffixlen == 0) {
424     return FALSE;
425   }
426 
427   if (suffixlen > slen) {
428     return FALSE;
429   }
430 
431   if (flags & PR_STR_FL_IGNORE_CASE) {
432     if (strncasecmp(s + (slen - suffixlen), suffix, suffixlen) == 0) {
433       res = TRUE;
434     }
435 
436   } else {
437     if (strncmp(s + (slen - suffixlen), suffix, suffixlen) == 0) {
438       res = TRUE;
439     }
440   }
441 
442   return res;
443 }
444 
pr_str_strip(pool * p,const char * str)445 const char *pr_str_strip(pool *p, const char *str) {
446   const char *dup_str, *start, *finish;
447   size_t len = 0;
448 
449   if (p == NULL ||
450       str == NULL) {
451     errno = EINVAL;
452     return NULL;
453   }
454 
455   /* First, find the non-whitespace start of the given string */
456   for (start = str; PR_ISSPACE(*start); start++);
457 
458   /* Now, find the non-whitespace end of the given string */
459   for (finish = &str[strlen(str)-1]; PR_ISSPACE(*finish); finish--);
460 
461   /* Include for the last byte, of course. */
462   len = finish - start + 1;
463 
464   /* The space-stripped string is, then, everything from start to finish. */
465   dup_str = pstrndup(p, start, len);
466 
467   return dup_str;
468 }
469 
pr_str_strip_end(char * s,const char * ch)470 char *pr_str_strip_end(char *s, const char *ch) {
471   size_t len;
472 
473   if (s == NULL ||
474       ch == NULL) {
475     errno = EINVAL;
476     return NULL;
477   }
478 
479   len = strlen(s);
480 
481   while (len && strchr(ch, *(s+len - 1))) {
482     pr_signals_handle();
483 
484     *(s+len - 1) = '\0';
485     len--;
486   }
487 
488   return s;
489 }
490 
491 #if defined(HAVE_STRTOULL) && \
492   (SIZEOF_UID_T == SIZEOF_LONG_LONG && SIZEOF_GID_T == SIZEOF_LONG_LONG)
parse_ull(const char * val,unsigned long long * num)493 static int parse_ull(const char *val, unsigned long long *num) {
494   char *endp = NULL;
495   unsigned long long res;
496 
497   res = strtoull(val, &endp, 10);
498   if (endp && *endp) {
499     errno = EINVAL;
500     return -1;
501   }
502 
503   *num = res;
504   return 0;
505 }
506 #endif /* HAVE_STRTOULL */
507 
parse_ul(const char * val,unsigned long * num)508 static int parse_ul(const char *val, unsigned long *num) {
509   char *endp = NULL;
510   unsigned long res;
511 
512   res = strtoul(val, &endp, 10);
513   if (endp && *endp) {
514     errno = EINVAL;
515     return -1;
516   }
517 
518   *num = res;
519   return 0;
520 }
521 
pr_str_bin2hex(pool * p,const unsigned char * buf,size_t len,int flags)522 char *pr_str_bin2hex(pool *p, const unsigned char *buf, size_t len, int flags) {
523   static const char *hex_lc = "0123456789abcdef", *hex_uc = "0123456789ABCDEF";
524   register unsigned int i;
525   const char *hex_vals;
526   char *hex, *ptr;
527   size_t hex_len;
528 
529   if (p == NULL ||
530       buf == NULL) {
531     errno = EINVAL;
532     return NULL;
533   }
534 
535   if (len == 0) {
536     return pstrdup(p, "");
537   }
538 
539   /* By default, we use lowercase hex values. */
540   hex_vals = hex_lc;
541   if (flags & PR_STR_FL_HEX_USE_UC) {
542     hex_vals = hex_uc;
543   }
544 
545   hex_len = (len * 2) + 1;
546   hex = palloc(p, hex_len);
547 
548   ptr = hex;
549   for (i = 0; i < len; i++) {
550     *ptr++ = hex_vals[buf[i] >> 4];
551     *ptr++ = hex_vals[buf[i] % 16];
552   }
553   *ptr = '\0';
554 
555   return hex;
556 }
557 
c2h(char c,unsigned char * h)558 static int c2h(char c, unsigned char *h) {
559   if (c >= '0' &&
560       c <= '9') {
561     *h = c - '0';
562     return TRUE;
563   }
564 
565   if (c >= 'a' &&
566       c <= 'f') {
567     *h = c - 'a' + 10;
568     return TRUE;
569   }
570 
571   if (c >= 'A' &&
572       c <= 'F') {
573     *h = c - 'A' + 10;
574     return TRUE;
575   }
576 
577   return FALSE;
578 }
579 
pr_str_hex2bin(pool * p,const unsigned char * hex,size_t hex_len,size_t * len)580 unsigned char *pr_str_hex2bin(pool *p, const unsigned char *hex, size_t hex_len,
581     size_t *len) {
582   register unsigned int i, j;
583   unsigned char *data;
584   size_t data_len;
585 
586   if (p == NULL ||
587       hex == NULL) {
588     errno = EINVAL;
589     return NULL;
590   }
591 
592   if (hex_len == 0) {
593     hex_len = strlen((char *) hex);
594   }
595 
596   if (hex_len == 0) {
597     data = (unsigned char *) pstrdup(p, "");
598     return data;
599   }
600 
601   data_len = hex_len / 2;
602   data = palloc(p, data_len);
603 
604   for (i = 0, j = 0; i < hex_len; i += 2) {
605     unsigned char v1, v2;
606 
607     if (c2h(hex[i], &v1) == FALSE) {
608       errno = ERANGE;
609       return NULL;
610     }
611 
612     if (c2h(hex[i+1], &v2) == FALSE) {
613       errno = ERANGE;
614       return NULL;
615     }
616 
617     data[j++] = ((v1 << 4) | v2);
618   }
619 
620   if (len != NULL) {
621     *len = data_len;
622   }
623 
624   return data;
625 }
626 
627 /* Calculate the Damerau-Levenshtein distance between strings `a' and `b'.
628  * This implementation borrows from the git implementation; see
629  * git/src/levenshtein.c.
630  */
pr_str_levenshtein(pool * p,const char * a,const char * b,int swap_cost,int subst_cost,int insert_cost,int del_cost,int flags)631 int pr_str_levenshtein(pool *p, const char *a, const char *b, int swap_cost,
632     int subst_cost, int insert_cost, int del_cost, int flags) {
633   size_t alen, blen;
634   unsigned int i, j;
635   int *row0, *row1, *row2, res;
636   pool *tmp_pool;
637 
638   if (p == NULL ||
639       a == NULL ||
640       b == NULL) {
641     errno = EINVAL;
642     return -1;
643   }
644 
645   alen = strlen(a);
646   blen = strlen(b);
647 
648   tmp_pool = make_sub_pool(p);
649   pr_pool_tag(tmp_pool, "Levenshtein Distance pool");
650 
651   if (flags & PR_STR_FL_IGNORE_CASE) {
652     char *a2, *b2;
653 
654     a2 = pstrdup(tmp_pool, a);
655     for (i = 0; i < alen; i++) {
656       a2[i] = tolower((int) a[i]);
657     }
658 
659     b2 = pstrdup(tmp_pool, b);
660     for (i = 0; i < blen; i++) {
661       b2[i] = tolower((int) b[i]);
662     }
663 
664     a = a2;
665     b = b2;
666   }
667 
668   row0 = pcalloc(tmp_pool, sizeof(int) * (blen + 1));
669   row1 = pcalloc(tmp_pool, sizeof(int) * (blen + 1));
670   row2 = pcalloc(tmp_pool, sizeof(int) * (blen + 1));
671 
672   for (j = 0; j <= blen; j++) {
673     row1[j] = j * insert_cost;
674   }
675 
676   for (i = 0; i < alen; i++) {
677     int *ptr;
678 
679     row2[0] = (i + 1) * del_cost;
680     for (j = 0; j < blen; j++) {
681       /* Substitution */
682       row2[j + 1] = row1[j] + (subst_cost * (a[i] != b[j]));
683 
684       /* Swap */
685       if (i > 0 &&
686           j > 0 &&
687           a[i-1] == b[j] &&
688           a[i] == b[j-1] &&
689           row2[j+1] > (row0[j-1] + swap_cost)) {
690         row2[j+1] = row0[j-1] + swap_cost;
691       }
692 
693       /* Deletion */
694       if (row2[j+1] > (row1[j+1] + del_cost)) {
695         row2[j+1] = row1[j+1] + del_cost;
696       }
697 
698       /* Insertion */
699       if (row2[j+1] > (row2[j] + insert_cost)) {
700         row2[j+1] = row2[j] + insert_cost;
701       }
702     }
703 
704     ptr = row0;
705     row0 = row1;
706     row1 = row2;
707     row2 = ptr;
708   }
709 
710   res = row2[blen];
711 
712   destroy_pool(tmp_pool);
713   return res;
714 }
715 
716 /* For tracking the Levenshtein distance for a string. */
717 struct candidate {
718   const char *s;
719   int distance;
720   int flags;
721 };
722 
distance_cmp(const void * a,const void * b)723 static int distance_cmp(const void *a, const void *b) {
724   const struct candidate *cand1, *cand2;
725   const char *s1, *s2;
726   int distance1, distance2;
727 
728   cand1 = *((const struct candidate **) a);
729   s1 = cand1->s;
730   distance1 = cand1->distance;
731 
732   cand2 = *((const struct candidate **) b);
733   s2 = cand2->s;
734   distance2 = cand2->distance;
735 
736   if (distance1 != distance2) {
737     return distance1 - distance2;
738   }
739 
740   if (cand1->flags & PR_STR_FL_IGNORE_CASE) {
741     return strcasecmp(s1, s2);
742   }
743 
744   return strcmp(s1, s2);
745 }
746 
pr_str_get_similars(pool * p,const char * s,array_header * candidates,int max_distance,int flags)747 array_header *pr_str_get_similars(pool *p, const char *s,
748     array_header *candidates, int max_distance, int flags) {
749   register unsigned int i;
750   size_t len;
751   array_header *similars;
752   struct candidate **distances;
753   pool *tmp_pool;
754 
755   if (p == NULL ||
756       s == NULL ||
757       candidates == NULL) {
758     errno = EINVAL;
759     return NULL;
760   }
761 
762   if (candidates->nelts == 0) {
763     errno = ENOENT;
764     return NULL;
765   }
766 
767   if (max_distance <= 0) {
768     max_distance = PR_STR_DEFAULT_MAX_EDIT_DISTANCE;
769   }
770 
771   tmp_pool = make_sub_pool(p);
772   pr_pool_tag(tmp_pool, "Similar Strings pool");
773 
774   /* In order to use qsort(3), we need a contiguous block of memory, not
775    * one of our array_headers.
776    */
777 
778   distances = pcalloc(tmp_pool, candidates->nelts * sizeof(struct candidate *));
779 
780   len = strlen(s);
781   for (i = 0; i < candidates->nelts; i++) {
782     const char *c;
783     struct candidate *cand;
784     int prefix_match = FALSE;
785 
786     c = ((const char **) candidates->elts)[i];
787     cand = pcalloc(tmp_pool, sizeof(struct candidate));
788     cand->s = c;
789     cand->flags = flags;
790 
791     /* Give prefix matches a higher score */
792     if (flags & PR_STR_FL_IGNORE_CASE) {
793       if (strncasecmp(c, s, len) == 0) {
794         prefix_match = TRUE;
795       }
796 
797     } else {
798       if (strncmp(c, s, len) == 0) {
799         prefix_match = TRUE;
800       }
801     }
802 
803     if (prefix_match == TRUE) {
804       cand->distance = 0;
805 
806     } else {
807       /* Note: We arbitrarily add one to the edit distance, in order to
808        * distinguish a distance of zero from our prefix match "distances" of
809        * zero above.
810        */
811       cand->distance = pr_str_levenshtein(tmp_pool, s, c, 0, 2, 1, 3,
812         flags) + 1;
813     }
814 
815     distances[i] = cand;
816   }
817 
818   qsort(distances, candidates->nelts, sizeof(struct candidate *), distance_cmp);
819 
820   similars = make_array(p, candidates->nelts, sizeof(const char *));
821   for (i = 0; i < candidates->nelts; i++) {
822     struct candidate *cand;
823 
824     cand = distances[i];
825     if (cand->distance <= max_distance) {
826       *((const char **) push_array(similars)) = cand->s;
827     }
828   }
829 
830   destroy_pool(tmp_pool);
831   return similars;
832 }
833 
pr_str_text_to_array(pool * p,const char * text,char delimiter)834 array_header *pr_str_text_to_array(pool *p, const char *text, char delimiter) {
835   char *ptr;
836   array_header *items;
837   size_t text_len;
838 
839   if (p == NULL ||
840       text == NULL) {
841     errno = EINVAL;
842     return NULL;
843   }
844 
845   text_len = strlen(text);
846   items = make_array(p, 1, sizeof(char *));
847 
848   if (text_len == 0) {
849     return items;
850   }
851 
852   ptr = memchr(text, delimiter, text_len);
853   while (ptr != NULL) {
854     size_t item_len;
855 
856     pr_signals_handle();
857 
858     item_len = ptr - text;
859     if (item_len > 0) {
860       char *item;
861 
862       item = palloc(p, item_len + 1);
863       memcpy(item, text, item_len);
864       item[item_len] = '\0';
865       *((char **) push_array(items)) = item;
866     }
867 
868     text = ++ptr;
869 
870     /* Include one byte for the delimiter character being skipped over. */
871     text_len = text_len - item_len - 1;
872 
873     if (text_len == 0) {
874       break;
875     }
876 
877     ptr = memchr(text, delimiter, text_len);
878   }
879 
880   if (text_len > 0) {
881     *((char **) push_array(items)) = pstrdup(p, text);
882   }
883 
884   return items;
885 }
886 
pr_str2uid(const char * val,uid_t * uid)887 int pr_str2uid(const char *val, uid_t *uid) {
888 #ifdef HAVE_STRTOULL
889   unsigned long long ull = 0ULL;
890 #endif /* HAVE_STRTOULL */
891   unsigned long ul = 0UL;
892 
893   if (val == NULL ||
894       uid == NULL) {
895     errno = EINVAL;
896     return -1;
897   }
898 
899 #if SIZEOF_UID_T == SIZEOF_LONG_LONG
900 # ifdef HAVE_STRTOULL
901   if (parse_ull(val, &ull) < 0) {
902     return -1;
903   }
904   *uid = ull;
905 
906 # else
907   if (parse_ul(val, &ul) < 0) {
908     return -1;
909   }
910   *uid = ul;
911 # endif /* HAVE_STRTOULL */
912 #else
913   (void) ull;
914   if (parse_ul(val, &ul) < 0) {
915     return -1;
916   }
917   *uid = ul;
918 #endif /* sizeof(uid_t) != sizeof(long long) */
919 
920   return 0;
921 }
922 
pr_str2gid(const char * val,gid_t * gid)923 int pr_str2gid(const char *val, gid_t *gid) {
924 #ifdef HAVE_STRTOULL
925   unsigned long long ull = 0ULL;
926 #endif /* HAVE_STRTOULL */
927   unsigned long ul = 0UL;
928 
929   if (val == NULL ||
930       gid == NULL) {
931     errno = EINVAL;
932     return -1;
933   }
934 
935 #if SIZEOF_GID_T == SIZEOF_LONG_LONG
936 # ifdef HAVE_STRTOULL
937   if (parse_ull(val, &ull) < 0) {
938     return -1;
939   }
940   *gid = ull;
941 
942 # else
943   if (parse_ul(val, &ul) < 0) {
944     return -1;
945   }
946   *gid = ul;
947 # endif /* HAVE_STRTOULL */
948 #else
949   (void) ull;
950   if (parse_ul(val, &ul) < 0) {
951     return -1;
952   }
953   *gid = ul;
954 #endif /* sizeof(gid_t) != sizeof(long long) */
955 
956   return 0;
957 }
958 
pr_uid2str(pool * p,uid_t uid)959 const char *pr_uid2str(pool *p, uid_t uid) {
960   static char buf[64];
961 
962   memset(&buf, 0, sizeof(buf));
963   if (uid != (uid_t) -1) {
964 #if SIZEOF_UID_T == SIZEOF_LONG_LONG
965     pr_snprintf(buf, sizeof(buf)-1, "%llu", (unsigned long long) uid);
966 #else
967     pr_snprintf(buf, sizeof(buf)-1, "%lu", (unsigned long) uid);
968 #endif /* sizeof(uid_t) != sizeof(long long) */
969   } else {
970     pr_snprintf(buf, sizeof(buf)-1, "%d", -1);
971   }
972 
973   if (p != NULL) {
974     return pstrdup(p, buf);
975   }
976 
977   return buf;
978 }
979 
pr_gid2str(pool * p,gid_t gid)980 const char *pr_gid2str(pool *p, gid_t gid) {
981   static char buf[64];
982 
983   memset(&buf, 0, sizeof(buf));
984   if (gid != (gid_t) -1) {
985 #if SIZEOF_GID_T == SIZEOF_LONG_LONG
986     pr_snprintf(buf, sizeof(buf)-1, "%llu", (unsigned long long) gid);
987 #else
988     pr_snprintf(buf, sizeof(buf)-1, "%lu", (unsigned long) gid);
989 #endif /* sizeof(gid_t) != sizeof(long long) */
990   } else {
991     pr_snprintf(buf, sizeof(buf)-1, "%d", -1);
992   }
993 
994   if (p != NULL) {
995     return pstrdup(p, buf);
996   }
997 
998   return buf;
999 }
1000 
1001 /* NOTE: Update mod_ban's ban_parse_timestr() to use this function. */
pr_str_get_duration(const char * str,int * duration)1002 int pr_str_get_duration(const char *str, int *duration) {
1003   int hours, mins, secs;
1004   int flags = PR_STR_FL_IGNORE_CASE, has_suffix = FALSE;
1005   size_t len;
1006   char *ptr = NULL;
1007 
1008   if (str == NULL) {
1009     errno = EINVAL;
1010     return -1;
1011   }
1012 
1013   if (sscanf(str, "%2d:%2d:%2d", &hours, &mins, &secs) == 3) {
1014     if (hours < 0 ||
1015         mins < 0 ||
1016         secs < 0) {
1017       errno = ERANGE;
1018       return -1;
1019     }
1020 
1021     if (duration != NULL) {
1022       *duration = (hours * 60 * 60) + (mins * 60) + secs;
1023     }
1024 
1025     return 0;
1026   }
1027 
1028   len = strlen(str);
1029   if (len == 0) {
1030     errno = EINVAL;
1031     return -1;
1032   }
1033 
1034   /* Handle the "single component" formats:
1035    *
1036    * If ends with "S", "s", or "sec": parse secs
1037    * If ends with "M", "m", or "min": parse minutes
1038    * If ends with "H", "h", or "hr": parse hours
1039    *
1040    * Otherwise, try to parse as just a number of seconds.
1041    */
1042 
1043   has_suffix = pr_strnrstr(str, len, "s", 1, flags);
1044   if (has_suffix == FALSE) {
1045     has_suffix = pr_strnrstr(str, len, "sec", 3, flags);
1046   }
1047   if (has_suffix == TRUE) {
1048     /* Parse seconds */
1049 
1050     if (sscanf(str, "%d", &secs) == 1) {
1051       if (secs < 0) {
1052         errno = ERANGE;
1053         return -1;
1054       }
1055 
1056       if (duration != NULL) {
1057         *duration = secs;
1058       }
1059 
1060       return 0;
1061     }
1062 
1063     errno = EINVAL;
1064     return -1;
1065   }
1066 
1067   has_suffix = pr_strnrstr(str, len, "m", 1, flags);
1068   if (has_suffix == FALSE) {
1069     has_suffix = pr_strnrstr(str, len, "min", 3, flags);
1070   }
1071   if (has_suffix == TRUE) {
1072     /* Parse minutes */
1073 
1074     if (sscanf(str, "%d", &mins) == 1) {
1075       if (mins < 0) {
1076         errno = ERANGE;
1077         return -1;
1078       }
1079 
1080       if (duration != NULL) {
1081         *duration = (mins * 60);
1082       }
1083 
1084       return 0;
1085     }
1086 
1087     errno = EINVAL;
1088     return -1;
1089   }
1090 
1091   has_suffix = pr_strnrstr(str, len, "h", 1, flags);
1092   if (has_suffix == FALSE) {
1093     has_suffix = pr_strnrstr(str, len, "hr", 2, flags);
1094   }
1095   if (has_suffix == TRUE) {
1096     /* Parse hours */
1097 
1098     if (sscanf(str, "%d", &hours) == 1) {
1099       if (hours < 0) {
1100         errno = ERANGE;
1101         return -1;
1102       }
1103 
1104       if (duration != NULL) {
1105         *duration = (hours * 60 * 60);
1106       }
1107 
1108       return 0;
1109     }
1110 
1111     errno = EINVAL;
1112     return -1;
1113   }
1114 
1115   /* Use strtol(3) here, check for trailing garbage, etc. */
1116   secs = (int) strtol(str, &ptr, 10);
1117   if (ptr && *ptr) {
1118     /* Not a bare number, but a string with non-numeric characters. */
1119     errno = EINVAL;
1120     return -1;
1121   }
1122 
1123   if (secs < 0) {
1124     errno = ERANGE;
1125     return -1;
1126   }
1127 
1128   if (duration != NULL) {
1129     *duration = secs;
1130   }
1131 
1132   return 0;
1133 }
1134 
pr_str_get_nbytes(const char * str,const char * units,off_t * nbytes)1135 int pr_str_get_nbytes(const char *str, const char *units, off_t *nbytes) {
1136   off_t sz;
1137   char *ptr = NULL;
1138   float factor = 0.0;
1139 
1140   if (str == NULL) {
1141     errno = EINVAL;
1142     return -1;
1143   }
1144 
1145   /* No negative numbers. */
1146   if (*str == '-') {
1147     errno = EINVAL;
1148     return -1;
1149   }
1150 
1151   if (units == NULL ||
1152       *units == '\0') {
1153     factor = 1.0;
1154 
1155   } else if (strncasecmp(units, "KB", 3) == 0) {
1156     factor = 1024.0;
1157 
1158   } else if (strncasecmp(units, "MB", 3) == 0) {
1159     factor = 1024.0 * 1024.0;
1160 
1161   } else if (strncasecmp(units, "GB", 3) == 0) {
1162     factor = 1024.0 * 1024.0 * 1024.0;
1163 
1164   } else if (strncasecmp(units, "TB", 3) == 0) {
1165     factor = 1024.0 * 1024.0 * 1024.0 * 1024.0;
1166 
1167   } else if (strncasecmp(units, "B", 2) == 0) {
1168     factor = 1.0;
1169 
1170   } else {
1171     errno = EINVAL;
1172     return -1;
1173   }
1174 
1175   errno = 0;
1176 
1177 #ifdef HAVE_STRTOULL
1178   sz = strtoull(str, &ptr, 10);
1179 #else
1180   sz = strtoul(str, &ptr, 10);
1181 #endif /* !HAVE_STRTOULL */
1182 
1183   if (errno == ERANGE) {
1184     return -1;
1185   }
1186 
1187   if (ptr != NULL && *ptr) {
1188     /* Error parsing the given string */
1189     errno = EINVAL;
1190     return -1;
1191   }
1192 
1193   /* Don't bother applying the factor if the result will overflow the result. */
1194 #ifdef ULLONG_MAX
1195   if (sz > (ULLONG_MAX / factor)) {
1196 #else
1197   if (sz > (ULONG_MAX / factor)) {
1198 #endif /* !ULLONG_MAX */
1199     errno = ERANGE;
1200     return -1;
1201   }
1202 
1203   if (nbytes != NULL) {
1204     *nbytes = (off_t) (sz * factor);
1205   }
1206 
1207   return 0;
1208 }
1209 
1210 char *pr_str_get_word(char **cp, int flags) {
1211   char *res, *dst;
1212   char quote_mode = 0;
1213 
1214   if (cp == NULL ||
1215      !*cp ||
1216      !**cp) {
1217     errno = EINVAL;
1218     return NULL;
1219   }
1220 
1221   if (!(flags & PR_STR_FL_PRESERVE_WHITESPACE)) {
1222     while (**cp && PR_ISSPACE(**cp)) {
1223       pr_signals_handle();
1224       (*cp)++;
1225     }
1226   }
1227 
1228   if (!**cp) {
1229     return NULL;
1230   }
1231 
1232   res = dst = *cp;
1233 
1234   if (!(flags & PR_STR_FL_PRESERVE_COMMENTS)) {
1235     /* Stop processing at start of an inline comment. */
1236     if (**cp == '#') {
1237       return NULL;
1238     }
1239   }
1240 
1241   if (**cp == '\"') {
1242     quote_mode++;
1243     (*cp)++;
1244   }
1245 
1246   while (**cp && (quote_mode ? (**cp != '\"') : !PR_ISSPACE(**cp))) {
1247     pr_signals_handle();
1248 
1249     if (**cp == '\\' && quote_mode) {
1250 
1251       /* Escaped char */
1252       if (*((*cp)+1)) {
1253         *dst = *(++(*cp));
1254       }
1255     }
1256 
1257     *dst++ = **cp;
1258     ++(*cp);
1259   }
1260 
1261   if (**cp) {
1262     (*cp)++;
1263   }
1264 
1265   *dst = '\0';
1266   return res;
1267 }
1268 
1269 /* get_token tokenizes a string, increments the src pointer to the next
1270  * non-separator in the string.  If the src string is empty or NULL, the next
1271  * token returned is NULL.
1272  */
1273 char *pr_str_get_token2(char **src, char *sep, size_t *token_len) {
1274   char *token;
1275   size_t len = 0;
1276 
1277   if (src == NULL ||
1278       *src == NULL ||
1279       **src == '\0' ||
1280       sep == NULL) {
1281 
1282     if (token_len != NULL) {
1283       *token_len = len;
1284     }
1285 
1286     errno = EINVAL;
1287     return NULL;
1288   }
1289 
1290   token = *src;
1291 
1292   while (**src && !strchr(sep, **src)) {
1293     (*src)++;
1294     len++;
1295   }
1296 
1297   if (**src) {
1298     *(*src)++ = '\0';
1299   }
1300 
1301   if (token_len != NULL) {
1302     *token_len = len;
1303   }
1304 
1305   return token;
1306 }
1307 
1308 char *pr_str_get_token(char **src, char *sep) {
1309   return pr_str_get_token2(src, sep, NULL);
1310 }
1311 
1312 int pr_str_is_boolean(const char *str) {
1313   if (str == NULL) {
1314     errno = EINVAL;
1315     return -1;
1316   }
1317 
1318   if (strncasecmp(str, "on", 3) == 0) {
1319     return TRUE;
1320   }
1321 
1322   if (strncasecmp(str, "off", 4) == 0) {
1323     return FALSE;
1324   }
1325 
1326   if (strncasecmp(str, "yes", 4) == 0) {
1327     return TRUE;
1328   }
1329 
1330   if (strncasecmp(str, "no", 3) == 0) {
1331     return FALSE;
1332   }
1333 
1334   if (strncasecmp(str, "true", 5) == 0) {
1335     return TRUE;
1336   }
1337 
1338   if (strncasecmp(str, "false", 6) == 0) {
1339     return FALSE;
1340   }
1341 
1342   if (strncasecmp(str, "1", 2) == 0) {
1343     return TRUE;
1344   }
1345 
1346   if (strncasecmp(str, "0", 2) == 0) {
1347     return FALSE;
1348   }
1349 
1350   errno = EINVAL;
1351   return -1;
1352 }
1353 
1354 /* Return true if str contains any of the glob(7) characters. */
1355 int pr_str_is_fnmatch(const char *str) {
1356   int have_bracket = 0;
1357 
1358   if (str == NULL) {
1359     return FALSE;
1360   }
1361 
1362   while (*str) {
1363     switch (*str) {
1364       case '?':
1365       case '*':
1366         return TRUE;
1367 
1368       case '\\':
1369         /* If the next character is NUL, we've reached the end of the string. */
1370         if (*(str+1) == '\0') {
1371           return FALSE;
1372         }
1373 
1374         /* Skip past the escaped character, i.e. the next character. */
1375         str++;
1376         break;
1377 
1378       case '[':
1379         have_bracket++;
1380         break;
1381 
1382       case ']':
1383         if (have_bracket)
1384           return TRUE;
1385         break;
1386 
1387       default:
1388         break;
1389     }
1390 
1391     str++;
1392   }
1393 
1394   return FALSE;
1395 }
1396 
1397