Marco Ivaldi <marco.ivaldi@mediaservice.net> has identified 3 vulnerabilities in CDE. Two of them could affect our CDE (open-source version), while the 3rd (sdtcm_convert) is Solaris specific. The two vulnerabilities, both of which affect dtsession could allow a local privilege escalation to root. A POC exists for Solaris. The POC will not function on our CDE for two main reasons: - the POC is Solaris specific - The overflowed variables in question are allocated on the heap, whereas in Solaris these variables are located on the stack. The first vulnerability allows an extra long palette name to be used to cause a crash via insufficient validation in SrvPalette.c:CheckMonitor(). The second, which has not yet been assigned a CERT CVE resides in SmCreateDirs.c:_DtCreateDtDirs() in libDtSvc. Due to insufficient bounds checking, a crash or corruption can be achieved by using a very long DISPLAY name. This one is considered difficult to exploit, and no POC code is available at this time. CDE 2.x code-bases are also listed as not vulnerable, however some work has been done anyway to do some proper bounds checking in this function. The following text portions are copied from the relevant advisories, which have not been released as of this writing. NOTE: Oracle CDE does NOT use CDE 2.3.0a or earlier as mentioned below. They are completely different code-bases): Regarding CVE-2020-2692: A buffer overflow in the CheckMonitor() function in the Common Desktop Environment 2.3.0a and earlier, as distributed with Oracle Solaris 10 1/13 (Update 11) and earlier, allows local users to gain root privileges via a long palette name passed to dtsession in a malicious .Xdefaults file. Note that Oracle Solaris CDE is based on the original CDE 1.x train, which is different from the CDE 2.x codebase that was later open sourced. Most notably, the vulnerable buffer in the Oracle Solaris CDE is stack-based, while in the open source version it is heap-based. Regarding the DtSvc bug, which does not currently have a CERT CVE: A difficult to exploit stack-based buffer overflow in the _DtCreateDtDirs() function in the Common Desktop Environment version distributed with Oracle Solaris 10 1/13 (Update 11) and earlier may allow local users to corrupt memory and potentially execute arbitrary code in order to escalate privileges via a long X11 display name. The vulnerable function is located in the libDtSvc library and can be reached by executing the setuid program dtsession. The open source version of CDE (based on the CDE 2.x codebase) is not affected.
391 lines
10 KiB
C
391 lines
10 KiB
C
/*
|
|
* CDE - Common Desktop Environment
|
|
*
|
|
* Copyright (c) 1993-2012, The Open Group. All rights reserved.
|
|
*
|
|
* These libraries and programs are free software; you can
|
|
* redistribute them and/or modify them under the terms of the GNU
|
|
* Lesser General Public License as published by the Free Software
|
|
* Foundation; either version 2 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* These libraries and programs are distributed in the hope that
|
|
* they will be useful, but WITHOUT ANY WARRANTY; without even the
|
|
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
* PURPOSE. See the GNU Lesser General Public License for more
|
|
* details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with these libraries and programs; if not, write
|
|
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
|
|
* Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
/* $TOG: SmCreateDirs.c /main/9 1997/02/24 09:23:16 barstow $ */
|
|
/* *
|
|
* (c) Copyright 1993, 1994 Hewlett-Packard Company *
|
|
* (c) Copyright 1993, 1994 International Business Machines Corp. *
|
|
* (c) Copyright 1993, 1994 Sun Microsystems, Inc. *
|
|
* (c) Copyright 1993, 1994 Novell, Inc. *
|
|
*/
|
|
/******************************************************************************
|
|
*
|
|
* File Name: SmCreateDirs.c
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#include <stdio.h>
|
|
#include <sys/param.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/wait.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <X11/Xlib.h>
|
|
#include <X11/Xlibint.h>
|
|
#include <X11/Intrinsic.h>
|
|
#include <Dt/DtNlUtils.h>
|
|
#include <Dt/DtPStrings.h>
|
|
#include <Dt/WsmP.h>
|
|
|
|
/******** Private Function Declarations ********/
|
|
|
|
static int
|
|
GetShortHostname (
|
|
char*,
|
|
unsigned int);
|
|
|
|
static char *
|
|
GetSessionDirProperty (
|
|
Display *display);
|
|
|
|
static char *
|
|
GetDisplayName (
|
|
Display *display);
|
|
|
|
|
|
/*************************************<->*************************************
|
|
*
|
|
* _DtCreateDtDirs (display)
|
|
*
|
|
* 1. Creates ~/.dt, ~/.dt/types, ~/.dt/tmp and either
|
|
* ~/.dt/sessions or ~/.dt/<display_name>
|
|
*
|
|
* 2. Returns the name of the session directory
|
|
*
|
|
* Outputs:
|
|
* -------
|
|
* Returns the session directory or NULL if malloc fails or ~/.dt
|
|
* cannot be created
|
|
*
|
|
*************************************<->***********************************/
|
|
|
|
char *
|
|
_DtCreateDtDirs(
|
|
Display *display )
|
|
{
|
|
char *tmpPath = NULL;
|
|
Boolean needSessionsDir = False;
|
|
Boolean useOldSession = False;
|
|
struct stat buf;
|
|
int status;
|
|
char *home = NULL;
|
|
char *sessionDir = NULL;
|
|
char *displayName = NULL;
|
|
|
|
/*
|
|
* Sanity check - make sure there's an existing display
|
|
*/
|
|
if(!display)
|
|
return(NULL);
|
|
|
|
if ((home = getenv("HOME")) == NULL)
|
|
home = "";
|
|
|
|
tmpPath = XtCalloc(1, MAXPATHLEN);
|
|
if(tmpPath == NULL)
|
|
return(NULL);
|
|
|
|
/*
|
|
* If the $HOME/.dt directory does not exist, create it
|
|
*/
|
|
snprintf(tmpPath, MAXPATHLEN, "%s/%s", home, DtPERSONAL_CONFIG_DIRECTORY);
|
|
|
|
status = stat(tmpPath, &buf);
|
|
if (status == -1) {
|
|
status = mkdir(tmpPath, 0000);
|
|
if (status == -1) {
|
|
XtFree(tmpPath);
|
|
return(NULL);
|
|
}
|
|
(void)chmod(tmpPath, 0755);
|
|
}
|
|
|
|
/*
|
|
* Create the personal DB directory if it does not exist.
|
|
*/
|
|
snprintf(tmpPath, MAXPATHLEN, "%s/%s", home, DtPERSONAL_DB_DIRECTORY);
|
|
|
|
if ((status = stat (tmpPath, &buf)) == -1) {
|
|
if ((status = mkdir (tmpPath, 0000)) != -1)
|
|
(void) chmod (tmpPath, 0755);
|
|
}
|
|
|
|
/*
|
|
* Create the personal tmp dir if it does not exist.
|
|
*/
|
|
snprintf(tmpPath, MAXPATHLEN, "%s/%s", home, DtPERSONAL_TMP_DIRECTORY);
|
|
|
|
if ((status = stat (tmpPath, &buf)) == -1) {
|
|
if ((status = mkdir (tmpPath, 0000)) != -1)
|
|
(void) chmod (tmpPath, 0755);
|
|
}
|
|
|
|
/*
|
|
* The creation of the session directory is tricky:
|
|
*
|
|
* For CDE 1.0 sessions, if a display-specific directory exists,
|
|
* it will take precedence. CDE 1.0 does not support the selection
|
|
* of a session.
|
|
*
|
|
* However, for newer CDE implementations, if a specific session
|
|
* was selected, the specified session will be used. If no session
|
|
* was selected, the CDE 1.0 mechanism will be used.
|
|
*
|
|
* If a CDEnext session is being used, the session directory will
|
|
* be on a property on the root window.
|
|
*
|
|
* Must check for this property and use it if is set.
|
|
*/
|
|
if ((sessionDir = GetSessionDirProperty (display)) != NULL) {
|
|
if (!strcmp (sessionDir, DtSM_SESSION_DIRECTORY)) {
|
|
/*
|
|
* Need to create a DtSM_SESSION_DIRECTORY dir if it
|
|
* does not exist.
|
|
*/
|
|
needSessionsDir = True;
|
|
|
|
} else if (!strcmp (sessionDir, DtSM_SESSION_DISPLAY_DIRECTORY)) {
|
|
/*
|
|
* Create a directory for a display-specific session if necessary
|
|
*/
|
|
if ((displayName = GetDisplayName (display)) != NULL) {
|
|
|
|
snprintf(tmpPath, MAXPATHLEN, "%s/%s/%s",
|
|
home,
|
|
DtPERSONAL_CONFIG_DIRECTORY,
|
|
displayName);
|
|
|
|
free(displayName); /* CDExc22771 */
|
|
displayName = NULL;
|
|
|
|
if ((status = stat (tmpPath, &buf)) == -1) {
|
|
if ((status = mkdir (tmpPath, 0000)) != -1)
|
|
(void) chmod (tmpPath, 0755);
|
|
else
|
|
useOldSession = True;
|
|
}
|
|
}
|
|
else {
|
|
/*
|
|
* Something's wrong with the display, use the fallback
|
|
*/
|
|
useOldSession = True;
|
|
}
|
|
} else {
|
|
/*
|
|
* The property contains an unrecognized value, fallback to
|
|
* other session selection algorithm.
|
|
*/
|
|
useOldSession = True;
|
|
}
|
|
XFree (sessionDir);
|
|
}
|
|
else
|
|
useOldSession = True;
|
|
|
|
if (useOldSession) {
|
|
/*
|
|
* Check for a display dependent directory. If one exists,
|
|
* it will be used.
|
|
*
|
|
* This is done for backward compatibility - THE DISPLAY
|
|
* DEPENDENT DIR TAKES PRECEDENT.
|
|
*/
|
|
if ((displayName = GetDisplayName (display)) != NULL) {
|
|
|
|
snprintf(tmpPath, MAXPATHLEN, "%s/%s/%s",
|
|
home,
|
|
DtPERSONAL_CONFIG_DIRECTORY,
|
|
displayName);
|
|
|
|
free(displayName); /* CDExc22771 */
|
|
displayName = NULL;
|
|
|
|
if ((status = stat(tmpPath, &buf)) != 0)
|
|
/*
|
|
* The display directory does not exist
|
|
*/
|
|
needSessionsDir = True;
|
|
}
|
|
else
|
|
needSessionsDir = True;
|
|
}
|
|
|
|
if(needSessionsDir)
|
|
{
|
|
/*
|
|
* If we don't have an old style directory - we check for a sessions
|
|
* directory, and create it if it doesn't exist
|
|
*/
|
|
snprintf(tmpPath, MAXPATHLEN, "%s/%s/%s",
|
|
home,
|
|
DtPERSONAL_CONFIG_DIRECTORY,
|
|
DtSM_SESSION_DIRECTORY);
|
|
|
|
if ((status = stat(tmpPath, &buf)) == -1) {
|
|
if ((status = mkdir(tmpPath, 0000)) == -1) {
|
|
XtFree(tmpPath);
|
|
return(NULL);
|
|
}
|
|
(void)chmod(tmpPath, 0755);
|
|
}
|
|
}
|
|
|
|
return(tmpPath);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------+*/
|
|
|
|
static int
|
|
GetShortHostname(
|
|
char *buffer,
|
|
unsigned int bufsize )
|
|
{
|
|
char * ptr;
|
|
int status;
|
|
|
|
if ((status = gethostname(buffer, bufsize)))
|
|
return status; /* failed gethostname */
|
|
if ((ptr = strstr(buffer, (char *)".")))
|
|
*ptr = '\0'; /* delete domain name if there is one */
|
|
return 0;
|
|
}
|
|
|
|
/*------------------------------------------------------------------------+*/
|
|
|
|
/*
|
|
* GetSessionDirProperty -
|
|
*/
|
|
static char *
|
|
GetSessionDirProperty (
|
|
Display *display)
|
|
{
|
|
int propStatus;
|
|
Atom actualType;
|
|
int actualFormat;
|
|
unsigned long nitems;
|
|
unsigned long leftover;
|
|
char *property = NULL;
|
|
Atom tmpAtom;
|
|
|
|
tmpAtom = XInternAtom(display, _XA_DT_RESTORE_DIR, False);
|
|
|
|
propStatus = XGetWindowProperty (display, RootWindow(display, 0),
|
|
(Atom) tmpAtom, 0L,
|
|
1000000L, False,
|
|
AnyPropertyType, &actualType,
|
|
&actualFormat, &nitems, &leftover,
|
|
(unsigned char **)&property);
|
|
|
|
if (propStatus == Success && actualType != None &&
|
|
actualFormat == 8 && nitems != 0)
|
|
return(property);
|
|
|
|
if (property)
|
|
XFree(property);
|
|
return (NULL);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------+*/
|
|
|
|
/*
|
|
* GetDisplayName -
|
|
*/
|
|
static char *
|
|
GetDisplayName (
|
|
Display *display)
|
|
{
|
|
char *tmpPath;
|
|
char hostName[101], displayName[101];
|
|
char *pch, *tmpNumber = NULL;
|
|
char *returnPath;
|
|
|
|
/*
|
|
* Create the display name and append it to the current path.
|
|
*/
|
|
snprintf(hostName, sizeof(hostName), "%s", display->display_name);
|
|
snprintf(displayName, sizeof(displayName), "%s", display->display_name);
|
|
|
|
/*
|
|
* If this is run to unix or local get the host name - otherwise
|
|
* just use what we have
|
|
*/
|
|
|
|
/*
|
|
* Strip host name to nothing but the unqualified (short) host name
|
|
*/
|
|
if ((pch = DtStrchr(hostName, ':')))
|
|
*pch = '\0';
|
|
|
|
if ((pch = DtStrchr(hostName, '.')))
|
|
*pch = '\0';
|
|
|
|
if((!strcmp(hostName, "unix")) || (!strcmp(hostName, "local"))
|
|
|| (!strcmp(hostName, "")))
|
|
{
|
|
/*
|
|
* host name is local - get the real name
|
|
*/
|
|
(void) GetShortHostname(hostName, 25);
|
|
}
|
|
|
|
/*
|
|
* Strip screen off of display name
|
|
*/
|
|
if ((tmpNumber = DtStrchr(displayName, ':')))
|
|
if ((pch = DtStrchr(tmpNumber, '.')))
|
|
*pch = '\0';
|
|
|
|
/*
|
|
* Strip it down to 14 chars (actually, 14 bytes or less)
|
|
*/
|
|
if((strlen(tmpNumber) + strlen(hostName)) > (size_t)14)
|
|
{
|
|
size_t tnLen;
|
|
int lastChLen;
|
|
char *lastCh;
|
|
|
|
/* Pare display number to at most 12 bytes */
|
|
while ((tnLen = strlen(tmpNumber)) > (size_t)12)
|
|
{
|
|
/* Remove the last character, an try again */
|
|
DtLastChar(tmpNumber, &lastCh, &lastChLen);
|
|
*lastCh = '\0';
|
|
}
|
|
|
|
/* Pare down host name, if necessary */
|
|
while ((strlen(hostName) + tnLen) > (size_t)14)
|
|
{
|
|
/* Remove the last character, and try again */
|
|
DtLastChar(hostName, &lastCh, &lastChLen);
|
|
*lastCh = '\0';
|
|
}
|
|
}
|
|
|
|
strcat (hostName, tmpNumber);
|
|
|
|
returnPath = strdup (hostName);
|
|
|
|
return (returnPath);
|
|
}
|