Commit message: None
CVE: CVE-2011-4328
CWE: CWE-264
Vulnerability description: plugin/npapi/plugin.cpp in Gnash before 0.8.10 uses weak permissions (world readable) for cookie files with predictable names in /tmp, which allows local users to obtain sensitive information.

Project: savannah

nsPluginInstance::DestroyStream(NPStream* /*stream*/, NPError /*reason*/)
{
    if (_streamfd != -1) {
        if (close(_streamfd) == -1) {
            perror("closing _streamfd");
        } else {
            _streamfd = -1;
        }
    }

    return NPERR_NO_ERROR;
}

Project: savannah

nsPluginInstance::GetValue(NPPVariable aVariable, void *aValue)
{

    if (aVariable == NPPVpluginScriptableNPObject) {
        if (_scriptObject) {
            void **v = (void **)aValue;
            NPN_RetainObject(_scriptObject);
            *v = _scriptObject;
        } else {
            gnash::log_debug("_scriptObject is not assigned");
        }
    }    


    return NS_PluginGetValue(aVariable, aValue);
}

Project: savannah

inline bool HasScripting()
{
    return (NPNFuncs.version >= NPVERS_HAS_NPRUNTIME_SCRIPTING);
}

Project: savannah

NS_DestroyPluginInstance(nsPluginInstanceBase* aPlugin)
{
    delete static_cast<gnash::nsPluginInstance *> (aPlugin);
}

Project: savannah

NS_NewPluginInstance(nsPluginCreateData * aCreateDataStruct)
{

    if(!aCreateDataStruct) {
        return NULL;
    }

    return new gnash::nsPluginInstance(aCreateDataStruct);
}

Project: savannah

NS_PluginGetValue(NPPVariable aVariable, void *aValue)
{
    NPError err = NPERR_NO_ERROR;

    switch (aVariable) {
      case NPPVpluginNameString:
          *static_cast<const char **> (aValue) = PLUGIN_NAME;
          break;
          
      case NPPVpluginDescriptionString:
          *static_cast<const char **>(aValue) = getPluginDescription();
          break;

      case NPPVpluginWindowBool:
          break;
          
      case NPPVpluginTimerInterval:
          break;
          
      case NPPVpluginKeepLibraryInMemory:
          break;
          
      case NPPVpluginNeedsXEmbed:
#ifdef HAVE_GTK2
          *static_cast<NPBool *>(aValue) = TRUE;
#else
          *static_cast<NPBool *>(aValue) = FALSE;
#endif
          break;

      case NPPVpluginScriptableNPObject:
          break;

#if NPAPI_VERSION != 190
      case NPPVpluginUrlRequestsDisplayedBool:
          break;
      case NPPVpluginWantsAllNetworkStreams:
          break;
#endif
          
      default:
          err = NPERR_INVALID_PARAM;
          break;
    }
    return err;
}

Project: savannah

NS_PluginInitialize()
{

    if ( gnash::plugInitialized ) {
        gnash::log_debug("NS_PluginInitialize called, but ignored (we already initialized)");
        return NPERR_NO_ERROR;
    }

    gnash::log_debug("NS_PluginInitialize call ---------------------------");


    NPError err = NPERR_NO_ERROR;
    NPBool supportsXEmbed = TRUE;

    err = NPN_GetValue(NULL,NPNVSupportsXEmbedBool,
                       (void *)&supportsXEmbed);

    if (err != NPERR_NO_ERROR || !supportsXEmbed) {
        gnash::log_error("NPAPI ERROR: No xEmbed support in this browser!");
        return NPERR_INCOMPATIBLE_VERSION_ERROR;
    } else {
        gnash::log_debug("xEmbed supported in this browser");
    }

    NPNToolkitType toolkit;
    err = NPN_GetValue(NULL, NPNVToolkit, &toolkit);

    if (err != NPERR_NO_ERROR || toolkit != NPNVGtk2) {
        gnash::log_error("NPAPI ERROR: No GTK2 support in this browser! Have version %d", (int)toolkit);
    } else {
        gnash::log_debug("GTK2 supported in this browser");
    }

    /*
    Check for environment variables.
    */
    char* opts = std::getenv("GNASH_OPTIONS");
    if (opts != NULL) {
        gnash::log_debug("GNASH_OPTIONS: %s", opts);
        
        if ( strstr(opts, "waitforgdb") ) {
            waitforgdb = true;
        }

        if ( strstr(opts, "writelauncher") ) {
            createSaLauncher = true;
        }

    }


    std::string newGnashRc;

#if !defined(__OS2__ ) && ! defined(__amigaos4__)
    newGnashRc.append(SYSCONFDIR);
    newGnashRc.append("/gnashpluginrc");
#endif

    const char *home = NULL;
#if defined(__amigaos4__)
    home = "/gnash";
#elif defined(__HAIKU__)
    BPath bp;
    if (B_OK != find_directory(B_USER_SETTINGS_DIRECTORY, &bp))
    {
        std::cerr << "Failed to find user settings directory" << std::endl;
    } else {
        bp.Append("Gnash");
        home = bp.Path();
    }
#else
    home = std::getenv("HOME");
#endif
    if ( home ) {
        newGnashRc.append(":");
        newGnashRc.append(home);
#ifdef __HAIKU__
        newGnashRc.append("/gnashpluginrc");
#else
        newGnashRc.append("/.gnashpluginrc");
#endif
    } else {
        gnash::log_error("WARNING: NPAPI plugin could not find user home dir");
    }

    char *gnashrc = std::getenv("GNASHRC");
    if ( gnashrc ) {
        newGnashRc.append(":");
        newGnashRc.append(gnashrc);
    }

    if ( setenv("GNASHRC", newGnashRc.c_str(), 1) ) {
        gnash::log_debug("WARNING: NPAPI plugin could not append to the GNASHRC env variable");
    } else {
        gnash::log_debug("NOTE: NPAPI plugin set GNASHRC to %d", newGnashRc);
    }

    /* Success */

    gnash::plugInitialized = TRUE;

    return NPERR_NO_ERROR;
}

Project: savannah

nsPluginInstance::NewStream(NPMIMEType /*type*/, NPStream* stream,
                            NPBool /*seekable*/, uint16_t* /*stype*/)
{

    if (_childpid) {
        return NPERR_GENERIC_ERROR;
    }
    _swf_url = stream->url;

    if (!_swf_url.empty() && _window) {
        return startProc();
    }

    return NPERR_NO_ERROR;
}

Project: savannah

nsPluginInstance::SetWindow(NPWindow* aWindow)
{
    if(!aWindow) {
        gnash::log_error(std::string(__FUNCTION__) + ": ERROR: Window handle was bogus!");
        return NPERR_INVALID_PARAM;
#if 0
    } else {
        gnash::log_debug("%s: X origin = %d, Y Origin = %d, Width = %d,"
            " Height = %d, WindowID = %p, this = %p",
            __FUNCTION__,
            aWindow->x, aWindow->y, aWindow->width, aWindow->height,
            aWindow->window, this);
#endif
    }

    if (_window) {
        return NPERR_GENERIC_ERROR;
    }

    _width = aWindow->width;
    _height = aWindow->height;

    _window = reinterpret_cast<Window> (aWindow->window);

    if (!_childpid && !_swf_url.empty()) {
        return startProc();
    }

    return NPERR_NO_ERROR;
}

Project: savannah

nsPluginInstance::WriteReady(NPStream* /* stream */ )
{
    if ( _streamfd != -1 ) {
	return 1024;
    } else {
	return 0;
    }
}

Project: savannah

cleanup_childpid(gpointer data)
{
    int* pid = static_cast<int*>(data);

    int status;
    int rv = waitpid(*pid, &status, WNOHANG);

    if (rv <= 0) {

        kill(*pid, SIGKILL);
        waitpid(*pid, &status, 0);
    }
 
    gnash::log_debug("Child process exited with status %s", status);

    delete pid;

    return FALSE;
}

Project: savannah

close_fds(const int (& except)[N])
{
    int numfailed = 0, closed = 0;
    for (int anfd = fileno(stderr)+1; numfailed < 10; anfd++) {
        if (std::find(except, except+N, anfd) != except+N) {
            continue;
        }
        if (close(anfd) < 0) {
            numfailed++;
        } else {
            numfailed = 0;
            closed++;
        }
    }
    gnash::log_debug("Closed %d files.", closed);
}

Project: savannah

create_standalone_launcher(const std::string& page_url, const std::string& swf_url,
                           const std::map<std::string, std::string>& params)
{
#ifdef CREATE_STANDALONE_GNASH_LAUNCHER
    if (!createSaLauncher) {
        return;
    }

    std::ofstream saLauncher;

    std::stringstream ss;
    static int debugno = 0;
    debugno = (debugno + 1) % 10;
    ss << "/tmp/gnash-debug-" << debugno << ".sh";
    saLauncher.open(ss.str().c_str(), std::ios::out | std::ios::trunc);

    if (!saLauncher) {
        gnash::log_error("Failed to open new file for standalone launcher: " + ss.str());
        return;
    }

    saLauncher << "#!/bin/sh" << std::endl
               << "export GNASH_COOKIES_IN="
               << "/tmp/gnash-cookies." << getpid() << std::endl
               << getGnashExecutable() << " ";

    if (!page_url.empty()) {
        saLauncher << "-U '" << page_url << "' ";
    }

    for (std::map<std::string,std::string>::const_iterator it = params.begin(),
        itEnd = params.end(); it != itEnd; ++it) {
        const std::string& nam = it->first; 
        const std::string& val = it->second;
        saLauncher << "-P '"
                   << boost::algorithm::replace_all_copy(nam, "'", "'\\''")
                   << "="
                   << boost::algorithm::replace_all_copy(val, "'", "'\\''")
                   << "' ";
    }

    saLauncher << "'" << swf_url << "' "
               << "$@"       // allow caller to pass any additional argument
               << std::endl;

    saLauncher.close();
#endif
}

Project: savannah

nsPluginInstance::getCurrentPageURL() const
{

    return getDocumentProp("baseURI");
}

Project: savannah

nsPluginInstance::getDocumentProp(const std::string& propname) const
{
    std::string rv;

    if (!HasScripting()) {
        LOG_ONCE( gnash::log_debug("Browser doesn't support scripting") );
        return rv;
    }

    NPObject* windowobj;
    NPError err = NPN_GetValue(_instance, NPNVWindowNPObject, &windowobj);
    if (err != NPERR_NO_ERROR || !windowobj) {
        return rv;
    }

    boost::shared_ptr<NPObject> window_obj(windowobj, NPN_ReleaseObject);
  
    NPIdentifier doc_id = NPN_GetStringIdentifier("document");

    NPVariant docvar;
    if(! NPN_GetProperty(_instance, windowobj, doc_id, &docvar) ) {
        return rv;
    }

    boost::shared_ptr<NPVariant> doc_var(&docvar, NPN_ReleaseVariantValue);

    if (!NPVARIANT_IS_OBJECT(docvar)) {
        return rv;
    }

    NPObject* doc_obj = NPVARIANT_TO_OBJECT(docvar);
    
    NPIdentifier prop_id = NPN_GetStringIdentifier(propname.c_str());

    NPVariant propvar;
    if (!NPN_GetProperty(_instance, doc_obj, prop_id, &propvar)) {
        return rv;
    }

    boost::shared_ptr<NPVariant> prop_var(&propvar, NPN_ReleaseVariantValue);

    if (!NPVARIANT_IS_STRING(propvar)) {
        return rv;
    }

    const NPString& prop_str = NPVARIANT_TO_STRING(propvar);

    rv = NPStringToString(prop_str);
    return rv;
}

Project: savannah

getGnashExecutable()
{
    std::string procname;
    bool process_found = false;
    struct stat procstats;

    char *gnash_env = std::getenv("GNASH_PLAYER");

    if (gnash_env) {
        procname = gnash_env;
        process_found = (0 == stat(procname.c_str(), &procstats));
        if (!process_found) {
            gnash::log_error("Invalid path to gnash executable: ");
            return "";
        }
    }

    if (!process_found) {
        procname = GNASHBINDIR "/gtk-gnash";
        process_found = (0 == stat(procname.c_str(), &procstats));
    }
    if (!process_found) {
        procname = GNASHBINDIR "/qt4-gnash";
        process_found = (0 == stat(procname.c_str(), &procstats));
    }

    if (!process_found) {
        gnash::log_error(std::string("Unable to find Gnash in ") + GNASHBINDIR);
        return "";
    }

    return procname;
}

Project: savannah

getPluginDescription() 
{
    static const char* desc = NULL;
    if (!desc) {
        desc = std::getenv("GNASH_PLUGIN_DESCRIPTION");
        if (desc == NULL) desc = PLUGIN_DESCRIPTION;
    }
    return desc;
}

Project: savannah

nsPluginInstance::handlePlayerRequests(GIOChannel* iochan, GIOCondition cond)
{

    if ( cond & G_IO_HUP ) {
        gnash::log_debug("Player control socket hang up");
        return false;
    }

    assert(cond & G_IO_IN);

    gnash::log_debug("Checking player requests on FD #%d",
              g_io_channel_unix_get_fd(iochan));

    const size_t buf_size = 1;
    gchar buffer[buf_size];

    do {
        GError* error = 0;
        gsize bytes_read = 0;

        GIOStatus status = g_io_channel_read_chars(iochan, buffer, buf_size,
                                                   &bytes_read, &error);

        switch (status) {
          case G_IO_STATUS_ERROR:
              gnash::log_error("error reading request line: %s",
                               error ? error->message : "unspecified error");
              g_error_free(error);
              return false;
          case G_IO_STATUS_EOF:
              gnash::log_error("EOF (error: %s)", 
                               error ? error->message : "unspecified error");
              g_error_free(error);
              return false;
          case G_IO_STATUS_AGAIN:
              gnash::log_debug("read again");
              continue;
          case G_IO_STATUS_NORMAL:
              _requestbuf.append(buffer, buffer+bytes_read);
#if 0
              gnash::log_debug("Normal read: %s", std::string(buffer, buffer+bytes_read));
#endif
              break;
          default:
              gnash::log_error("Abnormal status!");
              return false;
        }
    } while (g_io_channel_get_buffer_condition(iochan) & G_IO_IN);

    processPlayerRequest();
    
    return true;
}

Project: savannah

nsPluginInstance::nsPluginInstance(nsPluginCreateData* data)
    :
    nsPluginInstanceBase(),
    _instance(data->instance),
    _window(0),
    _width(0),
    _height(0),
    _streamfd(-1),
    _childpid(0),
    _filefd(-1),
    _name(),
    _scriptObject(0)
{

    for (size_t i=0, n=data->argc; i<n; ++i) {
        std::string name, val;
        gnash::StringNoCaseEqual noCaseCompare;

        if (data->argn[i]) {
            name = data->argn[i];
        }

        if (data->argv[i]) {
            val = data->argv[i];
        }

        if (noCaseCompare(name, "name")) {
            _name = val;
        }

        _params[name] = val;
    }

    if (HasScripting()) {
        _scriptObject = (GnashPluginScriptObject*) NPN_CreateObject( _instance,
            GnashPluginScriptObject::marshalGetNPClass());
    }
    
    return;
}

Project: savannah

processLog_debug(const boost::format& fmt)
{
    std::cout << "DEBUG: " << fmt.str() << std::endl;
}

Project: savannah

processLog_debug(const boost::format& /* fmt */)
{ /* do nothing */ }

Project: savannah

processLog_error(const boost::format& fmt)
{
    std::cerr << "ERROR: " << fmt.str() << std::endl;
}

Project: savannah

processLog_trace(const boost::format& fmt)
{
    std::cout << "TRACE: " << fmt.str() << std::endl;
}

Project: savannah

processLog_trace(const boost::format& /* fmt */)
{ /* do nothing */ }

Project: savannah

remove_handler(GIOChannel*, GIOCondition, gpointer)
{
    return FALSE;
}

Project: savannah

nsPluginInstance::setupIOChannel(int fd, GIOFunc handler, GIOCondition signals) const
{
    GIOChannel* ichan = g_io_channel_unix_new(fd);
    g_io_channel_set_close_on_unref(ichan, true);

    gnash::log_debug("New IO Channel for fd #%d",
                     g_io_channel_unix_get_fd(ichan));
    g_io_add_watch(ichan, signals, handler, (gpointer)this); 
    g_io_channel_unref(ichan);
}

Project: savannah

nsPluginInstance::setupProxy(const std::string& url)
{
#if NPAPI_VERSION != 190
    if (!NPNFuncs.getvalueforurl) return;
#endif

    char *proxy = 0;
    uint32_t length = 0;
#if NPAPI_VERSION != 190
    NPN_GetValueForURL(_instance, NPNURLVProxy, url.c_str(),
                       &proxy, &length);
#endif
    if (!proxy) {
        gnash::log_debug("No proxy setting for %s", url);
        return;
    }

    std::string nproxy (proxy, length);
    NPN_MemFree(proxy);

    gnash::log_debug("Proxy setting for %s is %s", url, nproxy);

    std::vector<std::string> parts;
    boost::split(parts, nproxy,
        boost::is_any_of(" "), boost::token_compress_on);
    if ( parts[0] == "DIRECT" ) {
    }
    else if ( parts[0] == "PROXY" ) {
        if (setenv("http_proxy", parts[1].c_str(), 1) < 0) {
            gnash::log_error(
                "Couldn't set environment variable http_proxy to %s",
                nproxy);
        }
    }
    else {
        gnash::log_error("Unknown proxy type: %s", nproxy);
    }

}

Project: savannah

nsPluginInstance::shut()
{
    gnash::log_debug("Gnash plugin shutting down");

    if (_streamfd != -1) {
        if (close(_streamfd) == -1) {
            perror("closing _streamfd");
        } else {
            _streamfd = -1;
        }
    }

}

Project: savannah

nsPluginInstance::startProc()
{

    int p2c_pipe[2];
    int c2p_pipe[2];
    int p2c_controlpipe[2];

    int ret = socketpair(AF_UNIX, SOCK_STREAM, 0, p2c_pipe);
    if (ret == -1) {
        gnash::log_error("socketpair(p2c) failed: %s", strerror(errno));
        return NPERR_GENERIC_ERROR;
    }
    _streamfd = p2c_pipe[1];

    ret = socketpair(AF_UNIX, SOCK_STREAM, 0, c2p_pipe);
    if (ret == -1) {
        gnash::log_error("socketpair(c2p) failed: %s", strerror(errno));
        return NPERR_GENERIC_ERROR;
    }

    ret = socketpair(AF_UNIX, SOCK_STREAM, 0, p2c_controlpipe);
    if (ret == -1) {
        gnash::log_error("socketpair(control) failed: %s", strerror(errno));
        return NPERR_GENERIC_ERROR;
    }

    if (HasScripting() && _scriptObject) {
        _scriptObject->setControlFD(p2c_controlpipe[1]);
        _scriptObject->setHostFD(c2p_pipe[0]);
    }
    

    std::vector<std::string> arg_vec = getCmdLine(c2p_pipe[1],
                                                  p2c_controlpipe[0]);

    if (arg_vec.empty()) {
        gnash::log_error("Failed to obtain command line parameters.");
        return NPERR_GENERIC_ERROR;
    }
    
    std::vector<const char*> args;
    
    std::transform(arg_vec.begin(), arg_vec.end(), std::back_inserter(args),
                   std::mem_fun_ref(&std::string::c_str));
    args.push_back(0);
    
    
    _childpid = fork();
    
    if (_childpid == -1) {
        gnash::log_error("fork() failed: %s", strerror(errno));
        return NPERR_OUT_OF_MEMORY_ERROR;
    }
    
    if (_childpid > 0) {
        
        ret = close (p2c_pipe[0]);
        if (ret == -1) {
            gnash::log_error("p2c_pipe[0] close() failed: %s",
                             strerror(errno));
        }
        
        ret = close (c2p_pipe[1]);
        if (ret == -1) {
            gnash::log_error("c2p_pipe[1] close() failed: %s",
                             strerror(errno));
            gnash::log_debug("Forked successfully but with ignorable errors.");
        } else {
            gnash::log_debug("Forked successfully, child process PID is %d",
                             _childpid);
        }

        setupIOChannel(c2p_pipe[0], (GIOFunc)handlePlayerRequestsWrapper,
                                    (GIOCondition)(G_IO_IN|G_IO_HUP));
        
        setupIOChannel(p2c_controlpipe[1], remove_handler, G_IO_HUP);

        return NPERR_NO_ERROR;
    }
    
    
    ret = close (p2c_pipe[1]);
    
    ret = dup2 (p2c_pipe[0], fileno(stdin));

    if (ret == -1) {
        gnash::log_error("dup2() failed: %s", strerror(errno));
    }
    
    int dontclose[] = {c2p_pipe[1], c2p_pipe[0], p2c_controlpipe[0]};
    close_fds(dontclose);

    
    gnash::log_debug("Starting process: %s", boost::algorithm::join(arg_vec, " "));

    wait_for_gdb();

    execv(args[0], const_cast<char**>(&args.front()));

    perror("executing standalone gnash");
    
    exit (-1);
}

Project: savannah

wait_for_gdb()
{
    if (!waitforgdb) {
        return;
    }


    std::cout << std::endl << "  Attach GDB to PID " << getpid()
              << " to debug!" << std::endl
              << "  This thread will block until then!" << std::endl
              << "  Once blocked here, you can set other breakpoints."
              << std::endl
              << "  Do a \"set variable waitforgdb=$false\" to continue"
              << std::endl << std::endl;

    while (waitforgdb) {
        sleep(1);
    }
}

Project: savannah

nsPluginInstance::~nsPluginInstance()
{

    if (_scriptObject) {
        NPN_ReleaseObject(_scriptObject);
    }

    do { } while (g_source_remove_by_user_data(this));

    if (_childpid > 0) {
        
        
        int status;
        int rv = waitpid(_childpid, &status, WNOHANG);

        if (rv <= 0) {
             int* pid = new int(_childpid);
             gnashSleep(1000);
	     cleanup_childpid(pid);
        } else {
            gnash::log_debug("Child process exited with status %d", status);
        }
    }
    _childpid = 0;
}