Commit message: Fix CVE-2011-4029: File permission change vulnerability.

Use fchmod() to change permissions of the lock file instead
of chmod(), thus avoid the race that can be exploited to set
a symbolic link to any file or directory in the system.

Signed-off-by: Matthieu Herrb <matthieu.herrb@laas.fr>
Reviewed-by: Alan Coopersmith <alan.coopersmith@oracle.com>
CVE: CVE-2011-4029
CWE: CWE-362
Vulnerability description: The LockServer function in os/utils.c in X.Org xserver before 1.11.2 allows local users to change the permissions of arbitrary files to 444, read those files, and possibly cause a denial of service (removed execution permission) via a symlink attack on a temporary lock file.

Project: xserver
File: utils.c

AdjustWaitForDelay (pointer waitTime, unsigned long newdelay)
{
    static struct timeval   delay_val;
    struct timeval	    **wt = (struct timeval **) waitTime;
    unsigned long	    olddelay;

    if (*wt == NULL)
    {
	delay_val.tv_sec = newdelay / 1000;
	delay_val.tv_usec = 1000 * (newdelay % 1000);
	*wt = &delay_val;
    }
    else
    {
	olddelay = (*wt)->tv_sec * 1000 + (*wt)->tv_usec / 1000;
	if (newdelay < olddelay)
	{
	    (*wt)->tv_sec = newdelay / 1000;
	    (*wt)->tv_usec = 1000 * (newdelay % 1000);
	}
    }
}

Project: xserver
File: utils.c

AutoResetServer (int sig)
{
    int olderrno = errno;

    dispatchException |= DE_RESET;
    isItTimeToYield = TRUE;
    errno = olderrno;
}

Project: xserver
File: utils.c

CheckUserAuthorization(void)
{
#ifdef USE_PAM
    static struct pam_conv conv = {
	misc_conv,
	NULL
    };

    pam_handle_t *pamh = NULL;
    struct passwd *pw;
    int retval;

    if (getuid() != geteuid()) {
	pw = getpwuid(getuid());
	if (pw == NULL)
	    FatalError("getpwuid() failed for uid %d\n", getuid());

	retval = pam_start("xserver", pw->pw_name, &conv, &pamh);
	if (retval != PAM_SUCCESS)
	    FatalError("pam_start() failed.\n"
			"\tMissing or mangled PAM config file or module?\n");

	retval = pam_authenticate(pamh, 0);
	if (retval != PAM_SUCCESS) {
	    pam_end(pamh, retval);
	    FatalError("PAM authentication failed, cannot start X server.\n"
			"\tPerhaps you do not have console ownership?\n");
	}

	retval = pam_acct_mgmt(pamh, 0);
	if (retval != PAM_SUCCESS) {
	    pam_end(pamh, retval);
	    FatalError("PAM authentication failed, cannot start X server.\n"
			"\tPerhaps you do not have console ownership?\n");
	}

	/* this is not a session, so do not do session management */
	pam_end(pamh, PAM_SUCCESS);
    }
#endif
}

Project: xserver
File: utils.c

CheckUserParameters(int argc, char **argv, char **envp)
{
    enum BadCode bad = NotBad;
    int i = 0, j;
    char *a, *e = NULL;

#if CHECK_EUID
    if (geteuid() == 0 && getuid() != geteuid())
#endif
    {
	/* Check each argv[] */
	for (i = 1; i < argc; i++) {
	    if (strcmp(argv[i], "-fp") == 0)
	    {
		i++; /* continue with next argument. skip the length check */
		if (i >= argc)
		    break;
	    } else
	    {
		if (strlen(argv[i]) > MAX_ARG_LENGTH) {
		    bad = ArgTooLong;
		    break;
		}
	    }
	    a = argv[i];
	    while (*a) {
		if (checkPrintable(*a) == 0) {
		    bad = UnprintableArg;
		    break;
		}
		a++;
	    }
	    if (bad)
		break;
	}
	if (!bad) {
	    /* Check each envp[] */
	    for (i = 0; envp[i]; i++) {

		/* Check for bad environment variables and values */
#if REMOVE_ENV_LD
		while (envp[i] && (strncmp(envp[i], "LD", 2) == 0)) {
		    for (j = i; envp[j]; j++) {
			envp[j] = envp[j+1];
		    }
		}
#endif   
		if (envp[i] && (strlen(envp[i]) > MAX_ENV_LENGTH)) {
#if REMOVE_LONG_ENV
		    for (j = i; envp[j]; j++) {
			envp[j] = envp[j+1];
		    }
		    i--;
#else
		    char *eq;
		    int len;

		    eq = strchr(envp[i], '=');
		    if (!eq)
			continue;
		    len = eq - envp[i];
		    e = malloc(len + 1);
		    if (!e) {
			bad = InternalError;
			break;
		    }
		    strncpy(e, envp[i], len);
		    e[len] = 0;
		    if (len >= 4 &&
			(strcmp(e + len - 4, "PATH") == 0 ||
			 strcmp(e, "TERMCAP") == 0)) {
			if (strlen(envp[i]) > MAX_ENV_PATH_LENGTH) {
			    bad = EnvTooLong;
			    break;
			} else {
			    free(e);
			}
		    } else {
			bad = EnvTooLong;
			break;
		    }
#endif
		}
	    }
	}
#if NO_OUTPUT_PIPES
	if (!bad) {
	    struct stat buf;

	    if (fstat(fileno(stdout), &buf) == 0 && S_ISFIFO(buf.st_mode))
		bad = OutputIsPipe;
	    if (fstat(fileno(stderr), &buf) == 0 && S_ISFIFO(buf.st_mode))
		bad = OutputIsPipe;
	}
#endif
    }
    switch (bad) {
    case NotBad:
	return;
    case UnsafeArg:
	ErrorF("Command line argument number %d is unsafe\n", i);
	break;
    case ArgTooLong:
	ErrorF("Command line argument number %d is too long\n", i);
	break;
    case UnprintableArg:
	ErrorF("Command line argument number %d contains unprintable"
		" characters\n", i);
	break;
    case EnvTooLong:
	ErrorF("Environment variable `%s' is too long\n", e);
	break;
    case OutputIsPipe:
	ErrorF("Stdout and/or stderr is a pipe\n");
	break;
    case InternalError:
	ErrorF("Internal Error\n");
	break;
    default:
	ErrorF("Unknown error\n");
	break;
    }
    FatalError("X server aborted because of unsafe environment\n");
}

Project: xserver
File: utils.c

Fclose(pointer iop)
{
#ifdef HAS_SAVED_IDS_AND_SETEUID
    return fclose(iop);
#else
    return Pclose(iop);
#endif
}

Project: xserver
File: utils.c

Fopen(char *file, char *type)
{
    FILE *iop;
#ifndef HAS_SAVED_IDS_AND_SETEUID
    struct pid *cur;
    int pdes[2], pid;

    if (file == NULL || type == NULL)
	return NULL;

    if ((*type != 'r' && *type != 'w') || type[1])
	return NULL;

    if ((cur = malloc(sizeof(struct pid))) == NULL)
	return NULL;

    if (pipe(pdes) < 0) {
	free(cur);
	return NULL;
    }

    switch (pid = fork()) {
    case -1: 	/* error */
	close(pdes[0]);
	close(pdes[1]);
	free(cur);
	return NULL;
    case 0:	/* child */
	if (setgid(getgid()) == -1)
	    _exit(127);
	if (setuid(getuid()) == -1)
	    _exit(127);
	if (*type == 'r') {
	    if (pdes[1] != 1) {
		/* stdout */
		dup2(pdes[1], 1);
		close(pdes[1]);
	    }
	    close(pdes[0]);
	} else {
	    if (pdes[0] != 0) {
		/* stdin */
		dup2(pdes[0], 0);
		close(pdes[0]);
	    }
	    close(pdes[1]);
	}
	execl("/bin/cat", "cat", file, (char *)NULL);
	_exit(127);
    }

    /* Avoid EINTR during stdio calls */
    OsBlockSignals ();
    
    /* parent */
    if (*type == 'r') {
	iop = fdopen(pdes[0], type);
	close(pdes[1]);
    } else {
	iop = fdopen(pdes[1], type);
	close(pdes[0]);
    }

    cur->fp = iop;
    cur->pid = pid;
    cur->next = pidlist;
    pidlist = cur;

    DebugF("Fopen(%s), fp = %p\n", file, iop);

    return iop;
#else
    int ruid, euid;

    ruid = getuid();
    euid = geteuid();
    
    if (seteuid(ruid) == -1) {
	    return NULL;
    }
    iop = fopen(file, type);

    if (seteuid(euid) == -1) {
	    fclose(iop);
	    return NULL;
    }
    return iop;
#endif /* HAS_SAVED_IDS_AND_SETEUID */
}

Project: xserver
File: utils.c

GetTimeInMillis (void)
{
  return GetTickCount ();
}

Project: xserver
File: utils.c

GetTimeInMillis(void)
{
    struct timeval tv;

#ifdef MONOTONIC_CLOCK
    struct timespec tp;
    static clockid_t clockid;
    if (!clockid) {
#ifdef CLOCK_MONOTONIC_COARSE
        if (clock_getres(CLOCK_MONOTONIC_COARSE, &tp) == 0 &&
            (tp.tv_nsec / 1000) <= 1000 &&
            clock_gettime(CLOCK_MONOTONIC_COARSE, &tp) == 0)
            clockid = CLOCK_MONOTONIC_COARSE;
        else
#endif
        if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0)
            clockid = CLOCK_MONOTONIC;
        else
            clockid = ~0L;
    }
    if (clockid != ~0L && clock_gettime(clockid, &tp) == 0)
        return (tp.tv_sec * 1000) + (tp.tv_nsec / 1000000L);
#endif

    X_GETTIMEOFDAY(&tv);
    return(tv.tv_sec * 1000) + (tv.tv_usec / 1000);
}

Project: xserver
File: utils.c

GiveUp(int sig)
{
    int olderrno = errno;

    dispatchException |= DE_TERMINATE;
    isItTimeToYield = TRUE;
    errno = olderrno;
}

Project: xserver
File: utils.c

OsAbort (void)
{
#ifndef __APPLE__
    OsBlockSignals();
#endif
    abort();
}

Project: xserver
File: utils.c

OsBlockSignals (void)
{
#ifdef SIG_BLOCK
    if (BlockedSignalCount++ == 0)
    {
	sigset_t    set;
	
	sigemptyset (&set);
	sigaddset (&set, SIGALRM);
	sigaddset (&set, SIGVTALRM);
#ifdef SIGWINCH
	sigaddset (&set, SIGWINCH);
#endif
#ifdef SIGIO
	sigaddset (&set, SIGIO);
#endif
	sigaddset (&set, SIGTSTP);
	sigaddset (&set, SIGTTIN);
	sigaddset (&set, SIGTTOU);
	sigaddset (&set, SIGCHLD);
	sigprocmask (SIG_BLOCK, &set, &PreviousSignalMask);
    }
#endif
}

Project: xserver
File: utils.c

OsReleaseSignals (void)
{
#ifdef SIG_BLOCK
    if (--BlockedSignalCount == 0)
    {
	sigprocmask (SIG_SETMASK, &PreviousSignalMask, 0);
    }
#endif
}

Project: xserver
File: utils.c

OsSignal(int sig, OsSigHandlerPtr handler)
{
    struct sigaction act, oact;

    sigemptyset(&act.sa_mask);
    if (handler != SIG_IGN)
	sigaddset(&act.sa_mask, sig);
    act.sa_flags = 0;
    act.sa_handler = handler;
    if (sigaction(sig, &act, &oact))
      perror("sigaction");
    return oact.sa_handler;
}

Project: xserver
File: utils.c

Pclose(pointer iop)
{
    struct pid *cur, *last;
    int pstat;
    int pid;

    DebugF("Pclose: fp = %p\n", iop);
    fclose(iop);

    for (last = NULL, cur = pidlist; cur; last = cur, cur = cur->next)
	if (cur->fp == iop)
	    break;
    if (cur == NULL)
	return -1;

    do {
	pid = waitpid(cur->pid, &pstat, 0);
    } while (pid == -1 && errno == EINTR);

    if (last == NULL)
	pidlist = cur->next;
    else
	last->next = cur->next;
    free(cur);

    /* allow EINTR again */
    OsReleaseSignals ();
    
    if (old_alarm && OsSignal(SIGALRM, old_alarm) == SIG_ERR) {
      perror("signal");
      return -1;
    }

    return pid == -1 ? -1 : pstat;
}

Project: xserver
File: utils.c

Popen(char *command, char *type)
{
    struct pid *cur;
    FILE *iop;
    int pdes[2], pid;

    if (command == NULL || type == NULL)
	return NULL;

    if ((*type != 'r' && *type != 'w') || type[1])
	return NULL;

    if ((cur = malloc(sizeof(struct pid))) == NULL)
	return NULL;

    if (pipe(pdes) < 0) {
	free(cur);
	return NULL;
    }

    /* Ignore the smart scheduler while this is going on */
    old_alarm = OsSignal(SIGALRM, SIG_IGN);
    if (old_alarm == SIG_ERR) {
      close(pdes[0]);
      close(pdes[1]);
      free(cur);
      perror("signal");
      return NULL;
    }

    switch (pid = fork()) {
    case -1: 	/* error */
	close(pdes[0]);
	close(pdes[1]);
	free(cur);
	if (OsSignal(SIGALRM, old_alarm) == SIG_ERR)
	  perror("signal");
	return NULL;
    case 0:	/* child */
	if (setgid(getgid()) == -1)
	    _exit(127);
	if (setuid(getuid()) == -1)
	    _exit(127);
	if (*type == 'r') {
	    if (pdes[1] != 1) {
		/* stdout */
		dup2(pdes[1], 1);
		close(pdes[1]);
	    }
	    close(pdes[0]);
	} else {
	    if (pdes[0] != 0) {
		/* stdin */
		dup2(pdes[0], 0);
		close(pdes[0]);
	    }
	    close(pdes[1]);
	}
	execl("/bin/sh", "sh", "-c", command, (char *)NULL);
	_exit(127);
    }

    /* Avoid EINTR during stdio calls */
    OsBlockSignals ();
    
    /* parent */
    if (*type == 'r') {
	iop = fdopen(pdes[0], type);
	close(pdes[1]);
    } else {
	iop = fdopen(pdes[1], type);
	close(pdes[0]);
    }

    cur->fp = iop;
    cur->pid = pid;
    cur->next = pidlist;
    pidlist = cur;

    DebugF("Popen: `%s', fp = %p\n", command, iop);

    return iop;
}

Project: xserver
File: utils.c

SmartScheduleInit (void)
{
    struct sigaction	act;

    if (SmartScheduleDisable)
	return;

    memset((char *) &act, 0, sizeof(struct sigaction));

    /* Set up the timer signal function */
    act.sa_handler = SmartScheduleTimer;
    sigemptyset (&act.sa_mask);
    sigaddset (&act.sa_mask, SIGALRM);
    if (sigaction (SIGALRM, &act, 0) < 0)
    {
	perror ("sigaction for smart scheduler");
	SmartScheduleDisable = TRUE;
    }
}

Project: xserver
File: utils.c

SmartScheduleStopTimer (void)
{
    struct itimerval	timer;
    
    if (SmartScheduleDisable)
	return;
    timer.it_interval.tv_sec = 0;
    timer.it_interval.tv_usec = 0;
    timer.it_value.tv_sec = 0;
    timer.it_value.tv_usec = 0;
    (void) setitimer (ITIMER_REAL, &timer, 0);
}

Project: xserver
File: utils.c

SmartScheduleTimer (int sig)
{
    SmartScheduleTime += SmartScheduleInterval;
}

Project: xserver
File: utils.c

System(char *command)
{
    int pid, p;
    void (*csig)(int);
    int status;

    if (!command)
	return 1;

    csig = signal(SIGCHLD, SIG_DFL);
    if (csig == SIG_ERR) {
      perror("signal");
      return -1;
    }
    DebugF("System: `%s'\n", command);

    switch (pid = fork()) {
    case -1:	/* error */
	p = -1;
    case 0:	/* child */
	if (setgid(getgid()) == -1)
	    _exit(127);
	if (setuid(getuid()) == -1)
	    _exit(127);
	execl("/bin/sh", "sh", "-c", command, (char *)NULL);
	_exit(127);
    default:	/* parent */
	do {
	    p = waitpid(pid, &status, 0);
	} while (p == -1 && errno == EINTR);
	
    }

    if (signal(SIGCHLD, csig) == SIG_ERR) {
      perror("signal");
      return -1;
    }

    return p == -1 ? -1 : status;
}

Project: xserver
File: utils.c

UnlockServer(void)
{
  if (nolock) return;

  if (!StillLocking){

  (void) unlink(LockFile);
  }
}

Project: xserver
File: utils.c

void UseMsg(void)
{
    ErrorF("use: X [:<display>] [option]\n");
    ErrorF("-a #                   default pointer acceleration (factor)\n");
    ErrorF("-ac                    disable access control restrictions\n");
    ErrorF("-audit int             set audit trail level\n");	
    ErrorF("-auth file             select authorization file\n");	
    ErrorF("-br                    create root window with black background\n");
    ErrorF("+bs                    enable any backing store support\n");
    ErrorF("-bs                    disable any backing store support\n");
    ErrorF("-c                     turns off key-click\n");
    ErrorF("c #                    key-click volume (0-100)\n");
    ErrorF("-cc int                default color visual class\n");
    ErrorF("-nocursor              disable the cursor\n");
    ErrorF("-core                  generate core dump on fatal error\n");
    ErrorF("-dpi int               screen resolution in dots per inch\n");
#ifdef DPMSExtension
    ErrorF("-dpms                  disables VESA DPMS monitor control\n");
#endif
    ErrorF("-deferglyphs [none|all|16] defer loading of [no|all|16-bit] glyphs\n");
    ErrorF("-f #                   bell base (0-100)\n");
    ErrorF("-fc string             cursor font\n");
    ErrorF("-fn string             default font name\n");
    ErrorF("-fp string             default font path\n");
    ErrorF("-help                  prints message with these options\n");
    ErrorF("-I                     ignore all remaining arguments\n");
#ifdef RLIMIT_DATA
    ErrorF("-ld int                limit data space to N Kb\n");
#endif
#ifdef RLIMIT_NOFILE
    ErrorF("-lf int                limit number of open files to N\n");
#endif
#ifdef RLIMIT_STACK
    ErrorF("-ls int                limit stack space to N Kb\n");
#endif
    ErrorF("-nolock                disable the locking mechanism\n");
    ErrorF("-nolisten string       don't listen on protocol\n");
    ErrorF("-noreset               don't reset after last client exists\n");
    ErrorF("-background [none]     create root window with no background\n");
    ErrorF("-reset                 reset after last client exists\n");
    ErrorF("-p #                   screen-saver pattern duration (minutes)\n");
    ErrorF("-pn                    accept failure to listen on all ports\n");
    ErrorF("-nopn                  reject failure to listen on all ports\n");
    ErrorF("-r                     turns off auto-repeat\n");
    ErrorF("r                      turns on auto-repeat \n");
    ErrorF("-render [default|mono|gray|color] set render color alloc policy\n");
    ErrorF("-retro                 start with classic stipple and cursor\n");
    ErrorF("-s #                   screen-saver timeout (minutes)\n");
    ErrorF("-seat string           seat to run on\n");
    ErrorF("-t #                   default pointer threshold (pixels/t)\n");
    ErrorF("-terminate             terminate at server reset\n");
    ErrorF("-to #                  connection time out\n");
    ErrorF("-tst                   disable testing extensions\n");
    ErrorF("ttyxx                  server started from init on /dev/ttyxx\n");
    ErrorF("v                      video blanking for screen-saver\n");
    ErrorF("-v                     screen-saver without video blanking\n");
    ErrorF("-wm                    WhenMapped default backing-store\n");
    ErrorF("-wr                    create root window with white background\n");
    ErrorF("-maxbigreqsize         set maximal bigrequest size \n");
#ifdef PANORAMIX
    ErrorF("+xinerama              Enable XINERAMA extension\n");
    ErrorF("-xinerama              Disable XINERAMA extension\n");
#endif
    ErrorF("-dumbSched             Disable smart scheduling, enable old behavior\n");
    ErrorF("-schedInterval int     Set scheduler interval in msec\n");
    ErrorF("-sigstop               Enable SIGSTOP based startup\n");
    ErrorF("+extension name        Enable extension\n");
    ErrorF("-extension name        Disable extension\n");
#ifdef XDMCP
    XdmcpUseMsg();
#endif
    XkbUseMsg();
    ddxUseMsg();
}

Project: xserver
File: utils.c

VerifyDisplayName(const char *d)
{
    if ( d == (char *)0 ) return 0;  /*  null  */
    if ( *d == '\0' ) return 0;  /*  empty  */
    if ( *d == '-' ) return 0;  /*  could be confused for an option  */
    if ( *d == '.' ) return 0;  /*  must not equal "." or ".."  */
    if ( strchr(d, '/') != (char *)0 ) return 0;  /*  very important!!!  */
    return 1;
}

Project: xserver
File: utils.c

XNFalloc(unsigned long amount)
{
    void *ptr = malloc(amount);
    if (!ptr)
        FatalError("Out of memory");
    return ptr;
}

Project: xserver
File: utils.c

XNFcalloc(unsigned long amount)
{
    void *ret = calloc(1, amount);
    if (!ret)
        FatalError("XNFcalloc: Out of memory");
    return ret;
}

Project: xserver
File: utils.c

XNFstrdup(const char *s)
{
    char *ret;

    if (s == NULL)
	return NULL;

    ret = strdup(s);
    if (!ret)
	FatalError("XNFstrdup: Out of memory");
    return ret;
}

Project: xserver
File: utils.c

Xalloc(unsigned long amount)
{
    /*
     * Xalloc used to return NULL when large amount of memory is requested. In
     * order to catch the buggy callers this warning has been added, slated to
     * removal by anyone who touches this code (or just looks at it) in 2011.
     *
     * -- Mikhail Gusarov
     */
    if ((long)amount <= 0)
	ErrorF("Warning: Xalloc: "
	       "requesting unpleasantly large amount of memory: %lu bytes.\n",
               amount);

    return malloc(amount);
}

Project: xserver
File: utils.c

Xcalloc(unsigned long amount)
{
    return calloc(1, amount);
}

Project: xserver
File: utils.c

Xfree(void *ptr)
{
    free(ptr);
}

Project: xserver
File: utils.c

Xrealloc(void *ptr, unsigned long amount)
{
    /*
     * Xrealloc used to return NULL when large amount of memory is requested. In
     * order to catch the buggy callers this warning has been added, slated to
     * removal by anyone who touches this code (or just looks at it) in 2011.
     *
     * -- Mikhail Gusarov
     */
    if ((long)amount <= 0)
	ErrorF("Warning: Xrealloc: "
	       "requesting unpleasantly large amount of memory: %lu bytes.\n",
               amount);

    return realloc(ptr, amount);
}

Project: xserver
File: utils.c

Xstrdup(const char *s)
{
    if (s == NULL)
	return NULL;
    return strdup(s);
}

Project: xserver
File: utils.c

set_font_authorizations(char **authorizations, int *authlen, pointer client)
{
#define AUTHORIZATION_NAME "hp-hostname-1"
#if defined(TCPCONN) || defined(STREAMSCONN)
    static char *result = NULL;
    static char *p = NULL;

    if (p == NULL)
    {
	char hname[1024], *hnameptr;
	unsigned int len;
#if defined(IPv6) && defined(AF_INET6)
	struct addrinfo hints, *ai = NULL;
#else
	struct hostent *host;
#ifdef XTHREADS_NEEDS_BYNAMEPARAMS
	_Xgethostbynameparams hparams;
#endif
#endif

	gethostname(hname, 1024);
#if defined(IPv6) && defined(AF_INET6)
	memset(&hints, 0, sizeof(hints));
	hints.ai_flags = AI_CANONNAME;
	if (getaddrinfo(hname, NULL, &hints, &ai) == 0) {
	    hnameptr = ai->ai_canonname;
	} else {
	    hnameptr = hname;
	}
#else
	host = _XGethostbyname(hname, hparams);
	if (host == NULL)
	    hnameptr = hname;
	else
	    hnameptr = host->h_name;
#endif

	len = strlen(hnameptr) + 1;
	result = malloc(len + sizeof(AUTHORIZATION_NAME) + 4);

	p = result;
        *p++ = sizeof(AUTHORIZATION_NAME) >> 8;
        *p++ = sizeof(AUTHORIZATION_NAME) & 0xff;
        *p++ = (len) >> 8;
        *p++ = (len & 0xff);

	memmove(p, AUTHORIZATION_NAME, sizeof(AUTHORIZATION_NAME));
	p += sizeof(AUTHORIZATION_NAME);
	memmove(p, hnameptr, len);
	p += len;
#if defined(IPv6) && defined(AF_INET6)
	if (ai) {
	    freeaddrinfo(ai);
	}
#endif
    }
    *authlen = p - result;
    *authorizations = result;
    return 1;
#else /* TCPCONN */
    return 0;
#endif /* TCPCONN */
}

Project: xserver
File: utils.c

xstrtokenize(const char *str, const char *separators)
{
    char **list, **nlist;
    char *tok, *tmp;
    unsigned num = 0, n;

    if (!str)
        return NULL;
    list = calloc(1, sizeof(*list));
    if (!list)
        return NULL;
    tmp = strdup(str);
    if (!tmp)
        goto error;
    for (tok = strtok(tmp, separators); tok; tok = strtok(NULL, separators)) {
        nlist = realloc(list, (num + 2) * sizeof(*list));
        if (!nlist)
            goto error;
        list = nlist;
        list[num] = strdup(tok);
        if (!list[num])
            goto error;
        list[++num] = NULL;
    }
    free(tmp);
    return list;

error:
    free(tmp);
    for (n = 0; n < num; n++)
        free(list[n]);
    free(list);
    return NULL;
}