+
+int isadir(const char* f)
+{
+ struct stat s;
+
+ if(stat(f, &s) < 0)
+ return 0;
+ else
+ return S_ISDIR(s.st_mode);
+}
+
+int is_safe_path(const char *file)
+{
+ char *p;
+ const char *f;
+ char x;
+ struct stat s;
+ char l[MAXBUFSIZE];
+
+ if(*file != '/')
+ {
+ syslog(LOG_ERR, _("`%s' is not an absolute path"), file);
+ return 0;
+ }
+
+ p = strrchr(file, '/');
+
+ if(p == file) /* It's in the root */
+ p++;
+
+ x = *p;
+ *p = '\0';
+
+ f = file;
+check1:
+ if(lstat(f, &s) < 0)
+ {
+ syslog(LOG_ERR, _("Couldn't stat `%s': %m"),
+ f);
+ return 0;
+ }
+
+ if(s.st_uid != geteuid())
+ {
+ syslog(LOG_ERR, _("`%s' is owned by UID %d instead of %d"),
+ f, s.st_uid, geteuid());
+ return 0;
+ }
+
+ if(S_ISLNK(s.st_mode))
+ {
+ syslog(LOG_WARNING, _("Warning: `%s' is a symlink"),
+ f);
+
+ if(readlink(f, l, MAXBUFSIZE) < 0)
+ {
+ syslog(LOG_ERR, _("Unable to read symbolic link `%s': %m"), f);
+ return 0;
+ }
+
+ f = l;
+ goto check1;
+ }
+
+ *p = x;
+ f = file;
+
+check2:
+ if(lstat(f, &s) < 0 && errno != ENOENT)
+ {
+ syslog(LOG_ERR, _("Couldn't stat `%s': %m"),
+ f);
+ return 0;
+ }
+
+ if(errno == ENOENT)
+ return 1;
+
+ if(s.st_uid != geteuid())
+ {
+ syslog(LOG_ERR, _("`%s' is owned by UID %d instead of %d"),
+ f, s.st_uid, geteuid());
+ return 0;
+ }
+
+ if(S_ISLNK(s.st_mode))
+ {
+ syslog(LOG_WARNING, _("Warning: `%s' is a symlink"),
+ f);
+
+ if(readlink(f, l, MAXBUFSIZE) < 0)
+ {
+ syslog(LOG_ERR, _("Unable to read symbolic link `%s': %m"), f);
+ return 0;
+ }
+
+ f = l;
+ goto check2;
+ }
+
+ if(s.st_mode & 0007)
+ {
+ /* Accessible by others */
+ syslog(LOG_ERR, _("`%s' has unsecure permissions"),
+ f);
+ return 0;
+ }
+
+ return 1;
+}
+
+FILE *ask_and_safe_open(const char* filename, const char* what)
+{
+ FILE *r;
+ char *directory;
+ char *fn;
+
+ /* Check stdin and stdout */
+ if(!isatty(0) || !isatty(1))
+ {
+ /* Argh, they are running us from a script or something. Write
+ the files to the current directory and let them burn in hell
+ for ever. */
+ fn = xstrdup(filename);
+ }
+ else
+ {
+ /* Ask for a file and/or directory name. */
+ fprintf(stdout, _("Please enter a file to save %s to [%s]: "),
+ what, filename);
+ fflush(stdout);
+
+ if((fn = readline(stdin, NULL, NULL)) == NULL)
+ {
+ fprintf(stderr, _("Error while reading stdin: %m\n"));
+ return NULL;
+ }
+
+ if(strlen(fn) == 0)
+ /* User just pressed enter. */
+ fn = xstrdup(filename);
+ }
+
+ if((strchr(fn, '/') == NULL) || (fn[0] != '/'))
+ {
+ /* The directory is a relative path or a filename. */
+ char *p;
+
+ directory = get_current_dir_name();
+ asprintf(&p, "%s/%s", directory, fn);
+ free(fn);
+ free(directory);
+ fn = p;
+ }
+
+ umask(0077); /* Disallow everything for group and other */
+
+ /* Open it first to keep the inode busy */
+ if((r = fopen(fn, "w")) == NULL)
+ {
+ fprintf(stderr, _("Error opening file `%s': %m\n"),
+ fn);
+ free(fn);
+ return NULL;
+ }
+
+ /* Then check the file for nasty attacks */
+ if(!is_safe_path(fn)) /* Do not permit any directories that are
+ readable or writeable by other users. */
+ {
+ fprintf(stderr, _("The file `%s' (or any of the leading directories) has unsafe permissions.\n"
+ "I will not create or overwrite this file.\n"),
+ fn);
+ fclose(r);
+ free(fn);
+ return NULL;
+ }
+
+ free(fn);
+
+ return r;
+}