X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=common-src%2Futil.c;h=24398f56c86919e1770f34379fae73a8ba09a83b;hb=2627875b7d18858bc1f9f7652811e4d8c15a23eb;hp=0eafec0b74d6822f7305e453b69c498eac97f789;hpb=1194fb66aa28d9929c3f2bef3cc6c1c3f40a60a4;p=debian%2Famanda diff --git a/common-src/util.c b/common-src/util.c index 0eafec0..24398f5 100644 --- a/common-src/util.c +++ b/common-src/util.c @@ -24,74 +24,224 @@ * file named AUTHORS, in the root directory of this distribution. */ /* - * $Id: util.c,v 1.17 2006/01/14 04:37:19 paddy_s Exp $ + * $Id: util.c,v 1.42 2006/08/24 01:57:15 paddy_s Exp $ */ #include "amanda.h" #include "util.h" +#include +#include "arglist.h" +#include "clock.h" +#include "sockaddr-util.h" +#include "conffile.h" +#include "base64.h" -/* - * Keep calling read() until we've read buflen's worth of data, or EOF, - * or we get an error. - * - * Returns the number of bytes read, 0 on EOF, or negative on error. - */ -ssize_t -fullread(fd, vbuf, buflen) - int fd; - void *vbuf; - size_t buflen; -{ - ssize_t nread, tot = 0; - char *buf = vbuf; /* cast to char so we can ++ it */ - - while (buflen > 0) { - nread = read(fd, buf, buflen); - if (nread < 0) { - if ((errno == EINTR) || (errno == EAGAIN)) - continue; - return ((tot > 0) ? tot : -1); - } +static int make_socket(sa_family_t family); +static int connect_port(sockaddr_union *addrp, in_port_t port, char *proto, + sockaddr_union *svaddr, int nonblock); - if (nread == 0) - break; +static int +make_socket( + sa_family_t family) +{ + int s; + int save_errno; +#if defined(SO_KEEPALIVE) || defined(USE_REUSEADDR) + int on=1; + int r; +#endif - tot += nread; - buf += nread; - buflen -= nread; + g_debug("make_socket opening socket with family %d", family); + s = socket(family, SOCK_STREAM, 0); + if (s == -1) { + save_errno = errno; + dbprintf(_("make_socket: socket() failed: %s\n"), strerror(save_errno)); + errno = save_errno; + return -1; + } + if (s < 0 || s >= (int)FD_SETSIZE) { + aclose(s); + errno = EMFILE; /* out of range */ + return -1; + } + +#ifdef USE_REUSEADDR + r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + if (r < 0) { + save_errno = errno; + dbprintf(_("make_socket: setsockopt(SO_REUSEADDR) failed: %s\n"), + strerror(errno)); + errno = save_errno; + } +#endif + +#ifdef SO_KEEPALIVE + r = setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, + (void *)&on, SIZEOF(on)); + if (r == -1) { + save_errno = errno; + dbprintf(_("make_socket: setsockopt() failed: %s\n"), + strerror(save_errno)); + aclose(s); + errno = save_errno; + return -1; } - return (tot); +#endif + + return s; } -/* - * Keep calling write() until we've written buflen's worth of data, - * or we get an error. - * - * Returns the number of bytes written, or negative on error. - */ -ssize_t -fullwrite(fd, vbuf, buflen) - int fd; - const void *vbuf; - size_t buflen; -{ - ssize_t nwritten, tot = 0; - const char *buf = vbuf; /* cast to char so we can ++ it */ - - while (buflen > 0) { - nwritten = write(fd, buf, buflen); - if (nwritten < 0) { - if ((errno == EINTR) || (errno == EAGAIN)) - continue; - return ((tot > 0) ? tot : -1); +/* addrp is my address */ +/* svaddr is the address of the remote machine */ +/* return socket on success */ +/* return -1 on failure */ +int +connect_portrange( + sockaddr_union *addrp, + in_port_t first_port, + in_port_t last_port, + char * proto, + sockaddr_union *svaddr, + int nonblock) +{ + int s; + in_port_t port; + static in_port_t port_in_use[1024]; + static int nb_port_in_use = 0; + int i; + int save_errno = EAGAIN; + + assert(first_port <= last_port); + /* Try a port already used */ + for(i=0; i < nb_port_in_use; i++) { + port = port_in_use[i]; + if(port >= first_port && port <= last_port) { + s = connect_port(addrp, port, proto, svaddr, nonblock); + if(s == -2) return -1; + if(s > 0) { + return s; + } + if (errno != EAGAIN && errno != EBUSY) + save_errno = errno; + } + } + + /* Try a port in the range */ + for (port = first_port; port <= last_port; port++) { + s = connect_port(addrp, port, proto, svaddr, nonblock); + if(s == -2) return -1; + if(s > 0) { + port_in_use[nb_port_in_use++] = port; + return s; + } + if (errno != EAGAIN && errno != EBUSY) + save_errno = errno; + } + + dbprintf(_("connect_portrange: All ports between %d and %d are busy.\n"), + first_port, + last_port); + errno = save_errno; + return -1; +} + +/* addrp is my address */ +/* svaddr is the address of the remote machine */ +/* return -2: Don't try again */ +/* return -1: Try with another port */ +/* return >0: this is the connected socket */ +int +connect_port( + sockaddr_union *addrp, + in_port_t port, + char * proto, + sockaddr_union *svaddr, + int nonblock) +{ + int save_errno; + struct servent * servPort; + socklen_t_equiv len; + socklen_t_equiv socklen; + int s; + + servPort = getservbyport((int)htons(port), proto); + if (servPort != NULL && !strstr(servPort->s_name, "amanda")) { + dbprintf(_("connect_port: Skip port %d: owned by %s.\n"), + port, servPort->s_name); + errno = EBUSY; + return -1; + } + + if ((s = make_socket(SU_GET_FAMILY(addrp))) == -1) return -2; + + SU_SET_PORT(addrp, port); + socklen = SS_LEN(addrp); + if (bind(s, (struct sockaddr *)addrp, socklen) != 0) { + save_errno = errno; + aclose(s); + if(servPort == NULL) { + dbprintf(_("connect_port: Try port %d: available - %s\n"), + port, strerror(errno)); + } else { + dbprintf(_("connect_port: Try port %d: owned by %s - %s\n"), + port, servPort->s_name, strerror(errno)); + } + if (save_errno != EADDRINUSE) { + errno = save_errno; + return -2; + } + + errno = save_errno; + return -1; + } + if(servPort == NULL) { + dbprintf(_("connect_port: Try port %d: available - Success\n"), port); + } else { + dbprintf(_("connect_port: Try port %d: owned by %s - Success\n"), + port, servPort->s_name); + } + + /* find out what port was actually used */ + + len = sizeof(*addrp); + if (getsockname(s, (struct sockaddr *)addrp, &len) == -1) { + save_errno = errno; + dbprintf(_("connect_port: getsockname() failed: %s\n"), + strerror(save_errno)); + aclose(s); + errno = save_errno; + return -1; + } + + if (nonblock) + fcntl(s, F_SETFL, fcntl(s, F_GETFL, 0)|O_NONBLOCK); + if (connect(s, (struct sockaddr *)svaddr, SS_LEN(svaddr)) == -1 && !nonblock) { + save_errno = errno; + dbprintf(_("connect_portrange: Connect from %s failed: %s\n"), + str_sockaddr(addrp), + strerror(save_errno)); + dbprintf(_("connect_portrange: connect to %s failed: %s\n"), + str_sockaddr(svaddr), + strerror(save_errno)); + aclose(s); + errno = save_errno; + if (save_errno == ECONNREFUSED || + save_errno == EHOSTUNREACH || + save_errno == ENETUNREACH || + save_errno == ETIMEDOUT) { + return -2 ; } - tot += nwritten; - buf += nwritten; - buflen -= nwritten; + return -1; } - return (tot); + + dbprintf(_("connected to %s\n"), + str_sockaddr(svaddr)); + dbprintf(_("our side is %s\n"), + str_sockaddr(addrp)); + return s; } + /* * Bind to a port in the given range. Takes a begin,end pair of port numbers. * @@ -99,112 +249,865 @@ fullwrite(fd, vbuf, buflen) * on success. */ int -bind_portrange(s, addrp, first_port, last_port, proto) - int s; - struct sockaddr_in *addrp; - int first_port, last_port; - char *proto; +bind_portrange( + int s, + sockaddr_union *addrp, + in_port_t first_port, + in_port_t last_port, + char * proto) { - int port, cnt; - const int num_ports = last_port - first_port + 1; - int save_errno; + in_port_t port; + in_port_t cnt; + socklen_t_equiv socklen; struct servent *servPort; + const in_port_t num_ports = (in_port_t)(last_port - first_port + 1); + int save_errno = EAGAIN; - assert(first_port > 0 && first_port <= last_port && last_port < 65536); + assert(first_port <= last_port); /* * We pick a different starting port based on our pid and the current * time to avoid always picking the same reserved port twice. */ - port = ((getpid() + time(0)) % num_ports) + first_port; + port = (in_port_t)(((getpid() + time(0)) % num_ports) + first_port); + /* * Scan through the range, trying all available ports that are either * not taken in /etc/services or registered for *amanda*. Wrap around * if we don't happen to start at the beginning. */ for (cnt = 0; cnt < num_ports; cnt++) { - servPort = getservbyport(htons(port), proto); - if((servPort == NULL) || strstr(servPort->s_name, "amanda")){ - dbprintf(("%s: bind_portrange2: trying port=%d\n", - debug_prefix_time(NULL), port)); - addrp->sin_port = htons(port); - if (bind(s, (struct sockaddr *)addrp, sizeof(*addrp)) >= 0) + servPort = getservbyport((int)htons(port), proto); + if ((servPort == NULL) || strstr(servPort->s_name, "amanda")) { + SU_SET_PORT(addrp, port); + socklen = SS_LEN(addrp); + if (bind(s, (struct sockaddr *)addrp, socklen) >= 0) { + if (servPort == NULL) { + dbprintf(_("bind_portrange2: Try port %d: Available - Success\n"), port); + } else { + dbprintf(_("bind_portrange2: Try port %d: Owned by %s - Success.\n"), port, servPort->s_name); + } return 0; - /* - * If the error was something other then port in use, stop. - */ - if (errno != EADDRINUSE) - break; + } + if (errno != EAGAIN && errno != EBUSY) + save_errno = errno; + if (servPort == NULL) { + dbprintf(_("bind_portrange2: Try port %d: Available - %s\n"), + port, strerror(errno)); + } else { + dbprintf(_("bind_portrange2: Try port %d: Owned by %s - %s\n"), + port, servPort->s_name, strerror(errno)); + } + } else { + dbprintf(_("bind_portrange2: Skip port %d: Owned by %s.\n"), + port, servPort->s_name); } if (++port > last_port) port = first_port; } - if (cnt == num_ports) { - dbprintf(("%s: bind_portrange: all ports between %d and %d busy\n", - debug_prefix_time(NULL), + dbprintf(_("bind_portrange: all ports between %d and %d busy\n"), first_port, - last_port)); - errno = EAGAIN; - } else if (last_port < IPPORT_RESERVED - && getuid() != 0 - && errno == EACCES) { + last_port); + errno = save_errno; + return -1; +} + +/* + * Writes out the entire iovec + */ +ssize_t +full_writev( + int fd, + struct iovec * iov, + int iovcnt) +{ + ssize_t delta, n, total; + + assert(iov != NULL); + + total = 0; + while (iovcnt > 0) { + /* + * Write the iovec + */ + n = writev(fd, iov, iovcnt); + if (n < 0) { + if (errno != EINTR) + return (-1); + } + else if (n == 0) { + errno = EIO; + return (-1); + } else { + total += n; + /* + * Iterate through each iov. Figure out what we still need + * to write out. + */ + for (; n > 0; iovcnt--, iov++) { + /* 'delta' is the bytes written from this iovec */ + delta = ((size_t)n < (size_t)iov->iov_len) ? n : (ssize_t)iov->iov_len; + /* subtract from the total num bytes written */ + n -= delta; + assert(n >= 0); + /* subtract from this iovec */ + iov->iov_len -= delta; + iov->iov_base = (char *)iov->iov_base + delta; + /* if this iovec isn't empty, run the writev again */ + if (iov->iov_len > 0) + break; + } + } + } + return (total); +} + + +int +needs_quotes( + const char * str) +{ + return (match("[ \t\f\r\n\"]", str) != 0); +} + + +/* + * For backward compatibility we are trying for minimal quoting. + * We only quote a string if it contains whitespace or is misquoted... + */ + +char * +quote_string( + const char *str) +{ + char * s; + char * ret; + + if ((str == NULL) || (*str == '\0')) { + ret = stralloc("\"\""); + } else if ((match("[:\'\\\"[:space:][:cntrl:]]", str)) == 0) { + /* + * String does not need to be quoted since it contains + * neither whitespace, control or quote characters. + */ + ret = stralloc(str); + } else { /* - * Do not bother with an error message in this case because it - * is expected. + * Allocate maximum possible string length. + * (a string of all quotes plus room for leading ", trailing " and NULL) */ + ret = s = alloc((strlen(str) * 2) + 2 + 1); + *(s++) = '"'; + while (*str != '\0') { + if (*str == '\t') { + *(s++) = '\\'; + *(s++) = 't'; + str++; + continue; + } else if (*str == '\n') { + *(s++) = '\\'; + *(s++) = 'n'; + str++; + continue; + } else if (*str == '\r') { + *(s++) = '\\'; + *(s++) = 'r'; + str++; + continue; + } else if (*str == '\f') { + *(s++) = '\\'; + *(s++) = 'f'; + str++; + continue; + } else if (*str == '\\') { + *(s++) = '\\'; + *(s++) = '\\'; + str++; + continue; + } + if (*str == '"') + *(s++) = '\\'; + *(s++) = *(str++); + } + *(s++) = '"'; + *s = '\0'; + } + return (ret); +} + + +char * +unquote_string( + const char *str) +{ + char * ret; + + if ((str == NULL) || (*str == '\0')) { + ret = stralloc(""); + } else { + char * in; + char * out; + + ret = in = out = stralloc(str); + while (*in != '\0') { + if (*in == '"') { + in++; + continue; + } + + if (*in == '\\') { + in++; + if (*in == 'n') { + in++; + *(out++) = '\n'; + continue; + } else if (*in == 't') { + in++; + *(out++) = '\t'; + continue; + } else if (*in == 'r') { + in++; + *(out++) = '\r'; + continue; + } else if (*in == 'f') { + in++; + *(out++) = '\f'; + continue; + } else if (*in >= '0' && *in <= '7') { + char c = 0; + int i = 0; + + while (i < 3 && *in >= '0' && *in <= '7') { + c = (c << 3) + *(in++) - '0'; + i++; + } + if (c) + *(out++) = c; + } else if (*in == '\0') { + /* trailing backslash -- ignore */ + break; + } + } + *(out++) = *(in++); + } + *out = '\0'; + } + return (ret); +} + +gchar ** +split_quoted_strings( + const gchar *string) +{ + char *local = g_strdup(string); + char *start = local; + char *p = local; + char **result; + GPtrArray *strs = g_ptr_array_new(); + int iq = 0; + + while (*p) { + if (!iq && *p == ' ') { + *p = '\0'; + g_ptr_array_add(strs, unquote_string(start)); + start = p+1; + } else if (*p == '\\') { + /* next character is taken literally; if it's a multicharacter + * escape (e.g., \171), that doesn't bother us here */ + p++; + if (!*p) break; + } else if (*p == '\"') { + iq = ! iq; + } + + p++; + } + if (start != string) + g_ptr_array_add(strs, unquote_string(start)); + + /* now convert strs into a strv, by stealing its references to the underlying + * strings */ + result = g_new0(char *, strs->len + 1); + memmove(result, strs->pdata, sizeof(char *) * strs->len); + + g_ptr_array_free(strs, FALSE); /* FALSE => don't free strings */ + g_free(local); + + return result; +} + +char * +strquotedstr(char **saveptr) +{ + char * tok = strtok_r(NULL, " ", saveptr); + size_t len; + int in_quote; + int in_backslash; + char *p, *t; + + if (!tok) + return tok; + len = strlen(tok); + in_quote = 0; + in_backslash = 0; + p = tok; + while (in_quote || in_backslash || *p != '\0') { + if (*p == '\0') { + /* append a new token */ + t = strtok_r(NULL, " ", saveptr); + if (!t) + return NULL; + tok[len] = ' '; + len = strlen(tok); + } + if (!in_backslash) { + if (*p == '"') + in_quote = !in_quote; + else if (*p == '\\') { + in_backslash = 1; + } + } else { + in_backslash = 0; + } + p++; + } + return tok; +} + +char * +sanitize_string( + const char *str) +{ + char * s; + char * ret; + + if ((str == NULL) || (*str == '\0')) { + ret = stralloc(""); } else { + ret = stralloc(str); + for (s = ret; *s != '\0'; s++) { + if (iscntrl((int)*s)) + *s = '?'; + } + } + return (ret); +} + +/* + Return 0 if the following characters are present + * ( ) < > [ ] , ; : ! $ \ / " + else returns 1 +*/ + +int +validate_mailto( + const char *mailto) +{ + return !match("\\*|<|>|\\(|\\)|\\[|\\]|,|;|:|\\\\|/|\"|\\!|\\$|\\|", mailto); +} + +int copy_file( + char *dst, + char *src, + char **errmsg) +{ + int infd, outfd; + int save_errno; + size_t nb; + char buf[32768]; + char *quoted; + + if ((infd = open(src, O_RDONLY)) == -1) { save_errno = errno; - dbprintf(("%s: bind_portrange: port %d: %s\n", - debug_prefix_time(NULL), - port, - strerror(errno))); - errno = save_errno; + quoted = quote_string(src); + *errmsg = vstrallocf(_("Can't open file '%s' for reading: %s"), + quoted, strerror(save_errno)); + amfree(quoted); + return -1; } - return -1; + + if ((outfd = open(dst, O_WRONLY|O_CREAT, 0600)) == -1) { + save_errno = errno; + quoted = quote_string(dst); + *errmsg = vstrallocf(_("Can't open file '%s' for writting: %s"), + quoted, strerror(save_errno)); + amfree(quoted); + close(infd); + return -1; + } + + while((nb=read(infd, &buf, SIZEOF(buf))) > 0) { + if(full_write(outfd,&buf,nb) < nb) { + save_errno = errno; + quoted = quote_string(dst); + *errmsg = vstrallocf(_("Error writing to '%s': %s"), + quoted, strerror(save_errno)); + amfree(quoted); + close(infd); + close(outfd); + return -1; + } + } + + if (errno != 0) { + save_errno = errno; + quoted = quote_string(src); + *errmsg = vstrallocf(_("Error reading from '%s': %s"), + quoted, strerror(save_errno)); + amfree(quoted); + close(infd); + close(outfd); + return -1; + } + + close(infd); + close(outfd); + return 0; } +#ifndef HAVE_READLINE /* - * Construct a datestamp (YYYYMMDD) from a time_t. + * simple readline() replacements, used when we don't have readline + * support from the system. */ + +char * +readline( + const char *prompt) +{ + g_printf("%s", prompt); + fflush(stdout); + fflush(stderr); + return agets(stdin); +} + +void +add_history( + const char *line) +{ + (void)line; /* Quiet unused parameter warning */ +} + +#endif + +/* Order of preference: readdir64(), readdir(). */ +#if HAVE_DECL_READDIR64 +# define USE_DIRENT64 +# define USE_READDIR64 +#elif HAVE_DECL_READDIR +# define USE_READDIR +#else +# error No readdir() or readdir64() available! +#endif + +char * portable_readdir(DIR* handle) { + +#ifdef USE_DIRENT64 + struct dirent64 *entry_p; +#else + struct dirent *entry_p; +#endif + + static GStaticMutex mutex = G_STATIC_MUTEX_INIT; + + g_static_mutex_lock(&mutex); + +#ifdef USE_READDIR + entry_p = readdir(handle); +#endif +#ifdef USE_READDIR64 + entry_p = readdir64(handle); +#endif + + g_static_mutex_unlock(&mutex); + + if (entry_p == NULL) + return NULL; + + /* FIXME: According to glibc documentation, d_name may not be + null-terminated in some cases on some very old platforms. Not + sure what to do about that case. */ + return strdup(entry_p->d_name); +} + +int search_directory(DIR * handle, const char * regex, + SearchDirectoryFunctor functor, gpointer user_data) { + int rval = 0; + regex_t compiled_regex; + gboolean done = FALSE; + + if (regcomp(&compiled_regex, regex, REG_EXTENDED | REG_NOSUB) != 0) { + regfree(&compiled_regex); + return -1; + } + + rewinddir(handle); + + while (!done) { + char * read_name; + int result; + read_name = portable_readdir(handle); + if (read_name == NULL) { + regfree(&compiled_regex); + return rval; + } + result = regexec(&compiled_regex, read_name, 0, NULL, 0); + if (result == 0) { + rval ++; + done = !functor(read_name, user_data); + } + amfree(read_name); + } + regfree(&compiled_regex); + return rval; +} + +char* find_regex_substring(const char* base_string, const regmatch_t match) { + char * rval; + int size; + + size = match.rm_eo - match.rm_so; + rval = malloc(size+1); + memcpy(rval, base_string + match.rm_so, size); + rval[size] = '\0'; + + return rval; +} + +int compare_possibly_null_strings(const char * a, const char * b) { + if (a == b) { + /* NULL or otherwise, they're the same. */ + return 0; + } else if (a == NULL) { + /* b != NULL */ + return -1; + } else if (b == NULL) { + /* a != NULL */ + return 1; + } else { + /* a != NULL != b */ + return strcmp(a, b); + } +} + +int +resolve_hostname(const char *hostname, + int socktype, + struct addrinfo **res, + char **canonname) +{ + struct addrinfo hints; + struct addrinfo *myres; + int flags = 0; + int result; + + if (res) *res = NULL; + if (canonname) { + *canonname = NULL; + flags = AI_CANONNAME; + } + +#ifdef AI_ADDRCONFIG + flags |= AI_ADDRCONFIG; +#endif + + memset(&hints, 0, sizeof(hints)); +#ifdef WORKING_IPV6 + /* get any kind of addresss */ + hints.ai_family = AF_UNSPEC; +#else + /* even if getaddrinfo supports IPv6, don't let it return + * such an address */ + hints.ai_family = AF_INET; +#endif + hints.ai_flags = flags; + hints.ai_socktype = socktype; + result = getaddrinfo(hostname, NULL, &hints, &myres); + if (result != 0) { + return result; + } + + if (canonname && myres && myres->ai_canonname) { + *canonname = stralloc(myres->ai_canonname); + } + + if (res) { + *res = myres; + } else { + freeaddrinfo(myres); + } + + return result; +} + char * -construct_datestamp(t) - time_t *t; +_str_exit_status( + char *subject, + amwait_t status) +{ + if (WIFEXITED(status)) { + int exitstatus = WEXITSTATUS(status); + if (exitstatus == 0) + return vstrallocf(_("%s exited normally"), subject); + else + return vstrallocf(_("%s exited with status %d"), subject, exitstatus); + } + + if (WIFSIGNALED(status)) { + int signal = WTERMSIG(status); +#ifdef WCOREDUMP + if (WCOREDUMP(status)) + return vstrallocf(_("%s exited after receiving signal %d (core dumped)"), + subject, signal); + else +#endif + return vstrallocf(_("%s exited after receiving signal %d"), + subject, signal); + } + + if (WIFSTOPPED(status)) { + int signal = WSTOPSIG(status); + return vstrallocf(_("%s stopped temporarily after receiving signal %d"), + subject, signal); + } + +#ifdef WIFCONTINUED + if (WIFCONTINUED(status)) { + return vstrallocf(_("%s was resumed"), subject); + } +#endif + + return vstrallocf(_("%s exited in unknown circumstances"), subject); +} + +void +check_running_as(running_as_flags who) { - struct tm *tm; - char datestamp[3*NUM_STR_SIZE]; - time_t when; +#ifdef CHECK_USERID + struct passwd *pw; + uid_t uid_me; + uid_t uid_target; + char *uname_me = NULL; + char *uname_target = NULL; + char *dumpuser; + + uid_me = getuid(); + if ((pw = getpwuid(uid_me)) == NULL) { + error(_("current userid %ld not found in password database"), (long)uid_me); + /* NOTREACHED */ + } + uname_me = stralloc(pw->pw_name); + +#ifndef SINGLE_USERID + if (!(who & RUNNING_AS_UID_ONLY) && uid_me != geteuid()) { + error(_("euid (%lld) does not match uid (%lld); is this program setuid-root when it shouldn't be?"), + (long long int)geteuid(), (long long int)uid_me); + /* NOTREACHED */ + } +#endif + + switch (who & RUNNING_AS_USER_MASK) { + case RUNNING_AS_ANY: + uid_target = uid_me; + uname_target = uname_me; + return; + + case RUNNING_AS_ROOT: + uid_target = 0; + uname_target = "root"; + break; + + case RUNNING_AS_DUMPUSER_PREFERRED: + dumpuser = getconf_str(CNF_DUMPUSER); + if ((pw = getpwnam(dumpuser)) != NULL && + uid_me != pw->pw_uid) { + if ((pw = getpwnam(CLIENT_LOGIN)) != NULL && + uid_me == pw->pw_uid) { + /* uid == CLIENT_LOGIN: not ideal, but OK */ + dbprintf(_("NOTE: running as '%s', which is the client" + " user, not the dumpuser ('%s'); forging" + " on anyway\n"), + CLIENT_LOGIN, dumpuser); + uid_target = uid_me; /* force success below */ + break; + } + } + /* FALLTHROUGH */ + + case RUNNING_AS_DUMPUSER: + uname_target = getconf_str(CNF_DUMPUSER); + if ((pw = getpwnam(uname_target)) == NULL) { + error(_("cannot look up dumpuser \"%s\""), uname_target); + /*NOTREACHED*/ + } + uid_target = pw->pw_uid; + break; + + case RUNNING_AS_CLIENT_LOGIN: + uname_target = CLIENT_LOGIN; + if ((pw = getpwnam(uname_target)) == NULL) { + error(_("cannot look up client user \"%s\""), uname_target); + /*NOTREACHED*/ + } + uid_target = pw->pw_uid; + break; + + default: + error(_("Unknown check_running_as() call")); + /* NOTREACHED */ + } + + if (uid_me != uid_target) { + error(_("running as user \"%s\" instead of \"%s\""), uname_me, uname_target); + /*NOTREACHED*/ + } + amfree(uname_me); + +#else + /* Quiet unused variable warning */ + (void)who; +#endif +} - if(t == NULL) { - when = time((time_t *)NULL); +int +set_root_privs(int need_root) +{ +#ifndef SINGLE_USERID + if (need_root) { + if (seteuid(0) == -1) return 0; + /* (we don't switch the group back) */ } else { - when = *t; + if (geteuid() != 0) return 0; + if (seteuid(getuid()) == -1) return 0; + if (setegid(getgid()) == -1) return 0; + } +#else + (void)need_root; /* Quiet unused variable warning */ +#endif + return 1; +} + +int +become_root(void) +{ +#ifndef SINGLE_USERID + // if euid !=0, it set only euid + if (setuid(0) == -1) return 0; + // will set ruid because euid == 0. + if (setuid(0) == -1) return 0; +#endif + return 1; +} + + +char * +base64_decode_alloc_string( + char *in) +{ + char *out; + size_t in_len = strlen(in); + size_t out_len = 3 * (in_len / 4) + 3; + + out = malloc(out_len); + if (!base64_decode(in, in_len, out, &out_len)) { + amfree(out); + return NULL; + } + out[out_len] = '\0'; + + return out; +} + + +/* A GHFunc (callback for g_hash_table_foreach) */ +void count_proplist( + gpointer key_p G_GNUC_UNUSED, + gpointer value_p, + gpointer user_data_p) +{ + property_t *value_s = value_p; + int *nb = user_data_p; + GSList *value; + + for(value=value_s->values; value != NULL; value = value->next) { + (*nb)++; } - tm = localtime(&when); - snprintf(datestamp, sizeof(datestamp), - "%04d%02d%02d", tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday); - return stralloc(datestamp); } +/* A GHFunc (callback for g_hash_table_foreach) */ +void proplist_add_to_argv( + gpointer key_p, + gpointer value_p, + gpointer user_data_p) +{ + char *property_s = key_p; + property_t *value_s = value_p; + char ***argv = user_data_p; + GSList *value; + char *q, *w, *qprop, *qvalue; + + q = quote_string(property_s); + /* convert to lower case */ + for (w=q; *w != '\0'; w++) { + *w = tolower(*w); + if (*w == '_') + *w = '-'; + } + qprop = stralloc2("--", q); + amfree(q); + for(value=value_s->values; value != NULL; value = value->next) { + qvalue = quote_string((char *)value->data); + **argv = stralloc(qprop); + (*argv)++; + **argv = qvalue; + (*argv)++; + } + amfree(qprop); +} + + /* - * Construct a timestamp (YYYYMMDDHHMMSS) from a time_t. + * Process parameters */ + +static char *pname = NULL; +static char *ptype = NULL; +static pcontext_t pcontext = CONTEXT_DEFAULT; + +void +set_pname(char *p) +{ + pname = newstralloc(pname, p); +} + char * -construct_timestamp(t) - time_t *t; +get_pname(void) { - struct tm *tm; - char timestamp[6*NUM_STR_SIZE]; - time_t when; + if (!pname) pname = stralloc("unknown"); + return pname; +} - if(t == NULL) { - when = time((time_t *)NULL); - } else { - when = *t; - } - tm = localtime(&when); - snprintf(timestamp, sizeof(timestamp), - "%04d%02d%02d%02d%02d%02d", - tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, - tm->tm_hour, tm->tm_min, tm->tm_sec); - return stralloc(timestamp); +void +set_ptype(char *p) +{ + ptype = newstralloc(ptype, p); +} + +char * +get_ptype(void) +{ + if (!ptype) ptype = stralloc("unknown"); + return ptype; +} + +void +set_pcontext(pcontext_t pc) +{ + pcontext = pc; +} + +pcontext_t +get_pcontext(void) +{ + return pcontext; }