1 /* $NetBSD: misc.c,v 1.1.1.1 2015/07/08 15:37:48 christos Exp $ */
3 /*****************************************************************
5 ** @(#) misc.c -- helper functions for the dnssec zone key tools
7 ** Copyright (c) Jan 2005, Holger Zuleger HZnet. All rights reserved.
9 ** This software is open source.
11 ** Redistribution and use in source and binary forms, with or without
12 ** modification, are permitted provided that the following conditions
15 ** Redistributions of source code must retain the above copyright notice,
16 ** this list of conditions and the following disclaimer.
18 ** Redistributions in binary form must reproduce the above copyright notice,
19 ** this list of conditions and the following disclaimer in the documentation
20 ** and/or other materials provided with the distribution.
22 ** Neither the name of Holger Zuleger HZnet nor the names of its contributors may
23 ** be used to endorse or promote products derived from this software without
24 ** specific prior written permission.
26 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 ** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 ** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
30 ** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 ** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 ** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 ** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 ** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 ** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 ** POSSIBILITY OF SUCH DAMAGE.
38 *****************************************************************/
42 # include <unistd.h> /* for link(), unlink() */
44 # include <sys/types.h>
45 # include <sys/stat.h>
54 # include "config_zkt.h"
62 # define TAINTEDCHARS "`$@;&<>|"
64 extern const char *progname
;
66 /*****************************************************************
67 ** getnameappendix (progname, basename)
68 ** return a pointer to the substring in progname subsequent
69 ** following "<basename>-".
70 *****************************************************************/
71 const char *getnameappendix (const char *progname
, const char *basename
)
76 assert (progname
!= NULL
);
77 assert (basename
!= NULL
);
79 if ( (p
= strrchr (progname
, '/')) != NULL
)
84 baselen
= strlen (basename
);
85 if ( strncmp (p
, basename
, baselen
-1) == 0 && *(p
+baselen
) == '-' )
95 /*****************************************************************
96 ** getdefconfname (view)
97 ** returns a pointer to a dynamic string containing the
98 ** default configuration file name
99 *****************************************************************/
100 const char *getdefconfname (const char *view
)
107 if ( (file
= getenv ("ZKT_CONFFILE")) == NULL
)
109 dbg_val2 ("getdefconfname (%s) file = %s\n", view
? view
: "NULL", file
);
111 if ( view
== NULL
|| *view
== '\0' || (p
= strrchr (file
, '.')) == NULL
)
112 return strdup (file
);
114 size
= strlen (file
) + strlen (view
) + 1 + 1;
115 if ( (buf
= malloc (size
)) == NULL
)
116 return strdup (file
);
118 dbg_val1 ("0123456789o123456789o123456789\tsize=%d\n", size
);
119 dbg_val4 ("%.*s-%s%s\n", p
- file
, file
, view
, p
);
121 snprintf (buf
, size
, "%.*s-%s%s", (int)(p
- file
), file
, view
, p
);
125 /*****************************************************************
126 ** domain_canonicdup (s)
127 ** returns NULL or a pointer to a dynamic string containing the
128 ** canonic (all lower case letters and ending with a '.')
130 *****************************************************************/
131 char *domain_canonicdup (const char *s
)
143 if ( len
> 0 && s
[len
-1] != '.' )
146 if ( (new = p
= malloc (len
+ 1)) == NULL
)
150 *p
++ = tolower (*s
++);
157 #if 0 /* replaced by domain_canonicdup */
158 /*****************************************************************
159 ** str_tolowerdup (s)
160 *****************************************************************/
161 char *str_tolowerdup (const char *s
)
166 if ( s
== NULL
|| (new = p
= malloc (strlen (s
) + 1)) == NULL
)
170 *p
++ = tolower (*s
++);
177 /*****************************************************************
179 ** Remove in string 's' all white space char
180 *****************************************************************/
181 char *str_delspace (char *s
)
186 if ( !s
) /* no string present ? */
190 for ( p
= s
; *p
; p
++ )
192 *s
++ = *p
; /* copy each nonspace */
194 *s
= '\0'; /* terminate string */
199 /*****************************************************************
200 ** in_strarr (str, arr, cnt)
201 ** check if string array 'arr' contains the string 'str'
202 ** return 1 if true or 'arr' or 'str' is empty, otherwise 0
203 *****************************************************************/
204 int in_strarr (const char *str
, char *const arr
[], int cnt
)
206 if ( arr
== NULL
|| cnt
<= 0 )
209 if ( str
== NULL
|| *str
== '\0' )
213 if ( strcmp (str
, arr
[cnt
]) == 0 )
219 /*****************************************************************
221 ** Remove in string 's' all TAINTED chars
222 *****************************************************************/
223 char *str_untaint (char *str
)
227 assert (str
!= NULL
);
229 for ( p
= str
; *p
; p
++ )
230 if ( strchr (TAINTEDCHARS
, *p
) )
235 /*****************************************************************
237 ** delete all occurrences of char 'c' at the end of string 's'
238 *****************************************************************/
239 char *str_chop (char *str
, char c
)
243 assert (str
!= NULL
);
245 len
= strlen (str
) - 1;
246 while ( len
>= 0 && str
[len
] == c
)
252 /*****************************************************************
253 ** parseurl (url, &proto, &host, &port, ¶ )
254 ** parses the given url (e.g. "proto://host.with.domain:port/para")
255 ** and set the pointer variables to the corresponding part of the string.
256 *****************************************************************/
257 void parseurl (char *url
, char **proto
, char **host
, char **port
, char **para
)
262 assert ( url
!= NULL
);
265 if ( (p
= strchr (url
, ':')) == NULL
) /* no protocol string given ? */
267 else /* looks like a protocol string */
268 if ( p
[1] == '/' && p
[2] == '/' ) /* protocol string ? */
275 else /* no protocol string found ! */
279 if ( *p
== '[' ) /* ipv6 address as hostname ? */
281 for ( start
= ++p
; *p
&& *p
!= ']'; p
++ )
287 for ( start
= p
; *p
&& *p
!= ':' && *p
!= '/'; p
++ )
296 for ( start
= p
; *p
&& isdigit (*p
); p
++ )
311 /*****************************************************************
312 ** splitpath (path, pathsize, filename)
313 ** if filename is build of "path/file" then copy filename to path
314 ** and split of the filename part.
315 ** return pointer to filename part in path or NULL if path is too
316 ** small to hold "path+filename"
317 *****************************************************************/
318 const char *splitpath (char *path
, size_t psize
, const char *filename
)
329 if ( (p
= strrchr (filename
, '/')) ) /* file arg contains path ? */
331 if ( strlen (filename
) + 1 > psize
)
334 strcpy (path
, filename
); /* copy whole filename to path */
335 path
[p
-filename
] = '\0'; /* split of the file part */
341 /*****************************************************************
342 ** pathname (path, size, dir, file, ext)
343 ** Concatenate 'dir', 'file' and 'ext' (if not null) to build
344 ** a pathname, and store the result in the character array
345 ** with length 'size' pointed to by 'path'.
346 *****************************************************************/
347 char *pathname (char *path
, size_t size
, const char *dir
, const char *file
, const char *ext
)
351 if ( path
== NULL
|| file
== NULL
)
354 len
= strlen (file
) + 1;
365 len
= sprintf (path
, "%s", dir
);
366 if ( path
[len
-1] != '/' )
378 /*****************************************************************
379 ** is_directory (name)
380 ** Check if the given pathname 'name' exists and is a directory.
382 *****************************************************************/
383 int is_directory (const char *name
)
387 if ( !name
|| !*name
)
390 return ( stat (name
, &st
) == 0 && S_ISDIR (st
.st_mode
) );
393 /*****************************************************************
395 ** Check if a file with the given pathname 'name' exists.
397 *****************************************************************/
398 int fileexist (const char *name
)
401 return ( stat (name
, &st
) == 0 && S_ISREG (st
.st_mode
) );
404 /*****************************************************************
406 ** return the size of the file with the given pathname 'name'.
407 ** returns -1 if the file not exist
408 *****************************************************************/
409 size_t filesize (const char *name
)
412 if ( stat (name
, &st
) == -1 )
414 return ( st
.st_size
);
417 /*****************************************************************
418 ** is_keyfilename (name)
419 ** Check if the given name looks like a dnssec (public)
420 ** keyfile name. Returns 0 | 1
421 *****************************************************************/
422 int is_keyfilename (const char *name
)
426 if ( name
== NULL
|| *name
!= 'K' )
430 if ( len
> 4 && strcmp (&name
[len
- 4], ".key") == 0 )
436 /*****************************************************************
437 ** is_dotfilename (name)
438 ** Check if the given pathname 'name' looks like "." or "..".
440 *****************************************************************/
441 int is_dotfilename (const char *name
)
444 (name
[0] == '.' && name
[1] == '\0') ||
445 (name
[0] == '.' && name
[1] == '.' && name
[2] == '\0')) )
451 /*****************************************************************
453 ** Set the modification time of the given pathname 'fname' to
454 ** 'sec'. Returns 0 on success.
455 *****************************************************************/
456 int touch (const char *fname
, time_t sec
)
460 utb
.actime
= utb
.modtime
= sec
;
461 return utime (fname
, &utb
);
464 /*****************************************************************
465 ** linkfile (fromfile, tofile)
466 *****************************************************************/
467 int linkfile (const char *fromfile
, const char *tofile
)
471 /* fprintf (stderr, "linkfile (%s, %s)\n", fromfile, tofile); */
472 if ( (ret
= link (fromfile
, tofile
)) == -1 && errno
== EEXIST
)
473 if ( unlink (tofile
) == 0 )
474 ret
= link (fromfile
, tofile
);
479 /*****************************************************************
480 ** copyfile (fromfile, tofile, dnskeyfile)
481 ** copy fromfile into tofile.
482 ** Add (optional) the content of dnskeyfile to tofile.
483 *****************************************************************/
484 int copyfile (const char *fromfile
, const char *tofile
, const char *dnskeyfile
)
490 /* fprintf (stderr, "copyfile (%s, %s)\n", fromfile, tofile); */
491 if ( (infp
= fopen (fromfile
, "r")) == NULL
)
493 if ( (outfp
= fopen (tofile
, "w")) == NULL
)
498 while ( (c
= getc (infp
)) != EOF
)
502 if ( dnskeyfile
&& *dnskeyfile
&& (infp
= fopen (dnskeyfile
, "r")) != NULL
)
504 while ( (c
= getc (infp
)) != EOF
)
513 /*****************************************************************
514 ** copyzonefile (fromfile, tofile, dnskeyfile)
515 ** copy a already signed zonefile and replace all zone DNSKEY
516 ** resource records by one "$INCLUDE dnskey.db" line
517 *****************************************************************/
518 int copyzonefile (const char *fromfile
, const char *tofile
, const char *dnskeyfile
)
524 int multi_line_dnskey
;
529 if ( fromfile
== NULL
)
532 if ( (infp
= fopen (fromfile
, "r")) == NULL
)
534 if ( tofile
== NULL
)
537 if ( (outfp
= fopen (tofile
, "w")) == NULL
)
544 multi_line_dnskey
= 0;
547 while ( fgets (buf
, sizeof buf
, infp
) != NULL
)
550 if ( !bufoverflow
&& !multi_line_dnskey
&& (*p
== '@' || isspace (*p
)) ) /* check if DNSKEY RR */
554 while ( isspace (*p
) ) ;
557 while ( isdigit (*p
) )
560 while ( isspace (*p
) )
564 if ( strncasecmp (p
, "IN", 2) == 0 )
567 while ( isspace (*p
) )
571 if ( strncasecmp (p
, "DNSKEY", 6) == 0 ) /* bingo! */
578 multi_line_dnskey
= 1;
580 multi_line_dnskey
= 0;
584 fprintf (outfp
, "$INCLUDE %s\n", dnskeyfile
);
592 fprintf (stderr
, "!! buffer overflow in copyzonefile() !!\n");
593 if ( !multi_line_dnskey
)
597 while ( *p
&& *p
!= ')' )
600 multi_line_dnskey
= 0;
605 bufoverflow
= buf
[len
-1] != '\n'; /* line too long ? */
616 /*****************************************************************
617 ** cmpfile (file1, file2)
618 ** returns -1 on error, 1 if the files differ and 0 if they
620 *****************************************************************/
621 int cmpfile (const char *file1
, const char *file2
)
628 /* fprintf (stderr, "cmpfile (%s, %s)\n", file1, file2); */
629 if ( (fp1
= fopen (file1
, "r")) == NULL
)
631 if ( (fp2
= fopen (file2
, "r")) == NULL
)
640 } while ( c1
!= EOF
&& c2
!= EOF
&& c1
== c2
);
650 /*****************************************************************
652 *****************************************************************/
653 int file_age (const char *fname
)
655 time_t curr
= time (NULL
);
656 time_t mtime
= file_mtime (fname
);
661 /*****************************************************************
662 ** file_mtime (fname)
663 *****************************************************************/
664 time_t file_mtime (const char *fname
)
668 if ( stat (fname
, &st
) < 0 )
673 /*****************************************************************
675 ** Check if we are running as root or if the file owner of
676 ** "prog" do not match the current user or the file permissions
677 ** allows file modification for others then the owner.
678 ** The same condition will be checked for the group ownership.
679 ** return 1 if the execution of the command "prog" will not
680 ** open a big security whole, 0 otherwise
681 *****************************************************************/
682 int is_exec_ok (const char *prog
)
687 if ( stat (prog
, &st
) < 0 )
690 curr_uid
= getuid ();
691 if ( curr_uid
== 0 ) /* don't run the cmd if we are root */
694 /* if the file owner and the current user matches and */
695 /* the file mode is not writable except for the owner, we are save */
696 if ( curr_uid
== st
.st_uid
&& (st
.st_mode
& (S_IWGRP
| S_IWOTH
)) == 0 )
699 /* if the file group and the current group matches and */
700 /* the file mode is not writable except for the group, we are also save */
701 if ( getgid() != st
.st_gid
&& (st
.st_mode
& (S_IWUSR
| S_IWOTH
)) == 0 )
707 /*****************************************************************
709 *****************************************************************/
710 void fatal (char *fmt
, ...)
716 fprintf (stderr
, "%s: ", progname
);
717 vfprintf (stderr
, fmt
, ap
);
722 /*****************************************************************
724 *****************************************************************/
725 void error (char *fmt
, ...)
730 vfprintf (stderr
, fmt
, ap
);
734 /*****************************************************************
735 ** logmesg (fmt, ...)
736 *****************************************************************/
737 void logmesg (char *fmt
, ...)
741 #if defined (LOG_WITH_PROGNAME) && LOG_WITH_PROGNAME
742 fprintf (stdout
, "%s: ", progname
);
745 vfprintf (stdout
, fmt
, ap
);
749 /*****************************************************************
750 ** verbmesg (verblvl, conf, fmt, ...)
751 *****************************************************************/
752 void verbmesg (int verblvl
, const zconf_t
*conf
, char *fmt
, ...)
759 vsnprintf (str
, sizeof (str
), fmt
, ap
);
762 //fprintf (stderr, "verbmesg (%d stdout=%d filelog=%d str = :%s:\n", verblvl, conf->verbosity, conf->verboselog, str);
763 if ( verblvl
<= conf
->verbosity
) /* check if we have to print this to stdout */
766 str_chop (str
, '\n');
767 if ( verblvl
<= conf
->verboselog
) /* check logging to syslog and/or file */
768 lg_mesg (LG_DEBUG
, str
);
772 /*****************************************************************
774 *****************************************************************/
780 /*****************************************************************
781 ** timestr2time (timestr)
782 ** timestr should look like "20071211223901" for 12 dec 2007 22:39:01
783 *****************************************************************/
784 time_t timestr2time (const char *timestr
)
789 // fprintf (stderr, "timestr = \"%s\"\n", timestr);
790 if ( sscanf (timestr
, "%4d%2d%2d%2d%2d%2d",
791 &t
.tm_year
, &t
.tm_mon
, &t
.tm_mday
,
792 &t
.tm_hour
, &t
.tm_min
, &t
.tm_sec
) != 6 )
798 #if defined(HAVE_TIMEGM) && HAVE_TIMEGM
806 snprintf (tzstr
, sizeof (tzstr
), "TZ=%s", "UTC");
811 snprintf (tzstr
, sizeof (tzstr
), "TZ=%s", tz
);
813 snprintf (tzstr
, sizeof (tzstr
), "TZ=%s", "");
819 return sec
< 0L ? 0L : sec
;
822 /*****************************************************************
823 ** time2str (sec, precison)
824 ** sec is seconds since 1.1.1970
825 ** precison is currently either 's' (for seconds) or 'm' (minutes)
826 *****************************************************************/
827 char *time2str (time_t sec
, int precision
)
830 static char timestr
[31+1]; /* 27+1 should be enough */
831 #if defined(HAVE_STRFTIME) && HAVE_STRFTIME
837 t
= localtime (&sec
);
838 if ( precision
== 's' )
839 strcpy (tformat
, "%b %d %Y %T");
841 strcpy (tformat
, "%b %d %Y %R");
843 strcat (tformat
, " %z");
845 strftime (timestr
, sizeof (timestr
), tformat
, t
);
847 #else /* no strftime available */
848 static char *mstr
[] = {
849 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
850 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
856 t
= localtime (&sec
);
861 s
= abs (t
->tm_gmtoff
);
862 h
= t
->tm_gmtoff
/ 3600;
863 s
= t
->tm_gmtoff
% 3600;
864 if ( precision
== 's' )
865 snprintf (timestr
, sizeof (timestr
), "%s %2d %4d %02d:%02d:%02d %c%02d%02d",
866 mstr
[t
->tm_mon
], t
->tm_mday
, t
->tm_year
+ 1900,
867 t
->tm_hour
, t
->tm_min
, t
->tm_sec
,
868 t
->tm_gmtoff
< 0 ? '-': '+',
871 snprintf (timestr
, sizeof (timestr
), "%s %2d %4d %02d:%02d %c%02d%02d",
872 mstr
[t
->tm_mon
], t
->tm_mday
, t
->tm_year
+ 1900,
873 t
->tm_hour
, t
->tm_min
,
874 t
->tm_gmtoff
< 0 ? '-': '+',
878 if ( precision
== 's' )
879 snprintf (timestr
, sizeof (timestr
), "%s %2d %4d %02d:%02d:%02d",
880 mstr
[t
->tm_mon
], t
->tm_mday
, t
->tm_year
+ 1900,
881 t
->tm_hour
, t
->tm_min
, t
->tm_sec
);
883 snprintf (timestr
, sizeof (timestr
), "%s %2d %4d %02d:%02d",
884 mstr
[t
->tm_mon
], t
->tm_mday
, t
->tm_year
+ 1900,
885 t
->tm_hour
, t
->tm_min
);
892 /*****************************************************************
893 ** time2isostr (sec, precison)
894 ** sec is seconds since 1.1.1970
895 ** precison is currently either 's' (for seconds) or 'm' (minutes)
896 *****************************************************************/
897 char *time2isostr (time_t sec
, int precision
)
900 static char timestr
[31+1]; /* 27+1 should be enough */
907 if ( precision
== 's' )
908 snprintf (timestr
, sizeof (timestr
), "%4d%02d%02d%02d%02d%02d",
909 t
->tm_year
+ 1900, t
->tm_mon
+1, t
->tm_mday
,
910 t
->tm_hour
, t
->tm_min
, t
->tm_sec
);
912 snprintf (timestr
, sizeof (timestr
), "%4d%02d%02d%02d%02d",
913 t
->tm_year
+ 1900, t
->tm_mon
+1, t
->tm_mday
,
914 t
->tm_hour
, t
->tm_min
);
919 /*****************************************************************
921 ** !!Attention: This function is not reentrant
922 *****************************************************************/
923 char *age2str (time_t sec
)
925 static char str
[20+1]; /* "2y51w6d23h50m55s" == 16+1 chars */
927 int strsize
= sizeof (str
);
930 # if PRINT_AGE_WITH_YEAR
931 if ( sec
/ (YEARSEC
) > 0 )
933 len
+= snprintf (str
+len
, strsize
- len
, "%1luy", sec
/ YEARSEC
);
937 len
+= snprintf (str
+len
, strsize
- len
, " ");
939 if ( sec
/ WEEKSEC
> 0 )
941 len
+= snprintf (str
+len
, strsize
- len
, "%2luw", (ulong
) sec
/ WEEKSEC
);
945 len
+= snprintf (str
+len
, strsize
- len
, " ");
946 if ( sec
/ DAYSEC
> 0 )
948 len
+= snprintf (str
+len
, strsize
- len
, "%2lud", sec
/ (ulong
)DAYSEC
);
952 len
+= snprintf (str
+len
, strsize
- len
, " ");
953 if ( sec
/ HOURSEC
> 0 )
955 len
+= snprintf (str
+len
, strsize
- len
, "%2luh", sec
/ (ulong
)HOURSEC
);
959 len
+= snprintf (str
+len
, strsize
- len
, " ");
960 if ( sec
/ MINSEC
> 0 )
962 len
+= snprintf (str
+len
, strsize
- len
, "%2lum", sec
/ (ulong
)MINSEC
);
966 len
+= snprintf (str
+len
, strsize
- len
, " ");
968 snprintf (str
+len
, strsize
- len
, "%2lus", (ulong
) sec
);
970 len
+= snprintf (str
+len
, strsize
- len
, " ");
975 /*****************************************************************
977 *****************************************************************/
978 time_t start_timer ()
983 /*****************************************************************
985 *****************************************************************/
986 time_t stop_timer (time_t start
)
988 time_t stop
= time (NULL
);
994 /****************************************************************
996 ** int gensalt (saltstr, sizeofsaltstr, bits)
998 ** generate a random hexstring of 'bits' salt and store it
999 ** in saltstr. return 1 on success, otherwise 0.
1001 *****************************************************************/
1002 int gensalt (char *salt
, size_t saltsize
, int saltbits
, unsigned int seed
)
1004 static char hexstr
[] = "0123456789ABCDEF";
1005 int saltlen
= 0; /* current length of salt in hex nibbles */
1010 srandom (seed
= (unsigned int)time (NULL
));
1012 saltlen
= saltbits
/ 4;
1013 if ( saltlen
+1 > saltsize
)
1016 for ( i
= 0; i
< saltlen
; i
++ )
1018 hex
= random () % 16;
1019 assert ( hex
>= 0 && hex
< 16 );
1020 salt
[i
] = hexstr
[hex
];
1028 #ifdef COPYZONE_TEST
1029 const char *progname
;
1030 main (int argc
, char *argv
[])
1034 if ( copyzonefile (argv
[1], NULL
) < 0 )
1035 error ("can't copy zone file %s\n", argv
[1]);
1040 const char *progname
;
1041 main (int argc
, char *argv
[])
1051 proto
= host
= port
= para
= NULL
;
1055 fprintf (stderr
, "usage: url_test <url>\n");
1056 fprintf (stderr
, "e.g.: url_test http://www.hznet.de:80/zkt\n");
1060 strcpy (url
, argv
[1]);
1061 parseurl (url
, &proto
, &host
, &port
, ¶
);
1064 printf ("proto: \"%s\"\n", proto
);
1066 printf ("host: \"%s\"\n", host
);
1068 printf ("port: \"%s\"\n", port
);
1070 printf ("para: \"%s\"\n", para
);