int main(int argc, char **argv) { char *p; struct timeval delta = { 1, 0 }; if (argc != 2) _usage(); __pmAFregister(&delta, NULL, timeout); #ifdef DBG_TRACE_DESPERATE pmDebug |= DBG_TRACE_DESPERATE; #endif for (p = argv[1]; *p; p++) { switch (*p) { case 'i': fprintf(stderr, "initialize\n"); PM_INIT_LOCKS(); break; case 'l': fprintf(stderr, "lock\n"); PM_LOCK(__pmLock_libpcp); break; case 'u': fprintf(stderr, "unlock\n"); PM_UNLOCK(__pmLock_libpcp); break; default: _usage(); } } return 0; }
const struct timeval * __pmDefaultRequestTimeout(void) { static int done_default = 0; PM_INIT_LOCKS(); PM_LOCK(__pmLock_libpcp); if (!done_default) { char *timeout_str; char *end_ptr; if ((timeout_str = getenv("PMCD_REQUEST_TIMEOUT")) != NULL) { def_timeout = strtod(timeout_str, &end_ptr); if (*end_ptr != '\0' || def_timeout < 0.0) { __pmNotifyErr(LOG_WARNING, "ignored bad PMCD_REQUEST_TIMEOUT = '%s'\n", timeout_str); } else { __pmtimevalFromReal(def_timeout, &def_wait); } } done_default = 1; } PM_UNLOCK(__pmLock_libpcp); return (&def_wait); }
int __pmSecureServerCertificateSetup(const char *db, const char *passwd, const char *cert_nickname) { PM_INIT_LOCKS(); PM_LOCK(secureserver_lock); /* Configure optional (cmdline) password file in case DB locked */ secure_server.password_file = passwd; /* * Configure location of the NSS database with a sane default. * For servers, we default to the shared (sql) system-wide database. * If command line db specified, pass it directly through - allowing * any old database format, at the users discretion. */ if (db) { /* shortened-buffer-size (-2) guarantees null-termination */ strncpy(secure_server.database_path, db, MAXPATHLEN-2); } if (cert_nickname) { strncpy(secure_server.cert_nickname, cert_nickname, MAX_CERT_NAME_LENGTH-2); } else { strncpy(secure_server.cert_nickname, SECURE_SERVER_CERTIFICATE, MAX_CERT_NAME_LENGTH-2); } PM_UNLOCK(secureserver_lock); return 0; }
void __pmConfig(__pmConfigCallback formatter) { /* * Scan ${PCP_CONF-$PCP_DIR/etc/pcp.conf} and put all PCP config * variables found therein into the environment. */ FILE *fp; char confpath[32]; char dir[MAXPATHLEN]; char var[MAXPATHLEN]; char *prefix; char *conf; char *val; char *p; PM_INIT_LOCKS(); PM_LOCK(__pmLock_libpcp); prefix = getenv("PCP_DIR"); if ((conf = getenv("PCP_CONF")) == NULL) { strncpy(confpath, "/etc/pcp.conf", sizeof(confpath)); if (prefix == NULL) conf = __pmNativePath(confpath); else { snprintf(dir, sizeof(dir), "%s%s", prefix, __pmNativePath(confpath)); conf = dir; } } if (access((const char *)conf, R_OK) < 0 || (fp = fopen(conf, "r")) == (FILE *)NULL) { char errmsg[PM_MAXERRMSGLEN]; pmprintf("FATAL PCP ERROR: could not open config file \"%s\" : %s\n", conf, osstrerror_r(errmsg, sizeof(errmsg))); pmprintf("You may need to set PCP_CONF or PCP_DIR in your environment.\n"); pmflush(); PM_UNLOCK(__pmLock_libpcp); exit(1); } while (fgets(var, sizeof(var), fp) != NULL) { if (var[0] == '#' || (p = strchr(var, '=')) == NULL) continue; *p = '\0'; val = p+1; if ((p = strrchr(val, '\n')) != NULL) *p = '\0'; if ((p = getenv(var)) != NULL) val = p; else formatter(var, prefix, val); if (pmDebug & DBG_TRACE_CONFIG) fprintf(stderr, "pmGetConfig: (init) %s=%s\n", var, val); } fclose(fp); PM_UNLOCK(__pmLock_libpcp); }
int __pmUnpinPDUBuf(void *handle) { bufctl_t *pcp, pcp_search; void *bcp; assert(((__psint_t)handle % sizeof(int)) == 0); PM_INIT_LOCKS(); PM_LOCK(__pmLock_libpcp); /* * Initialize a dummy bufctl_t to use only as search key; * only its bc_buf & bc_size fields need to be set, as that's * all that bufctl_t_compare will look at. */ pcp_search.bc_buf = handle; pcp_search.bc_size = 1; bcp = tfind(&pcp_search, &buf_tree, &bufctl_t_compare); /* * NB: don't release the lock until final disposition of this object; * we don't want to play TOCTOU. */ if (likely(bcp != NULL)) { pcp = *(bufctl_t **)bcp; } else { #ifdef PCP_DEBUG if (pmDebug & DBG_TRACE_PDUBUF) { fprintf(stderr, "__pmUnpinPDUBuf(" PRINTF_P_PFX "%p) -> fails\n", handle); pdubufdump(); } #endif PM_UNLOCK(__pmLock_libpcp); return 0; } #ifdef PCP_DEBUG if (unlikely(pmDebug & DBG_TRACE_PDUBUF)) fprintf(stderr, "__pmUnpinPDUBuf(" PRINTF_P_PFX "%p) -> pdubuf=" PRINTF_P_PFX "%p, pincnt=%d\n", handle, pcp->bc_buf, pcp->bc_pincnt - 1); #endif assert((&pcp->bc_buf[0] <= (char*)handle) && ((char*)handle < &pcp->bc_buf[pcp->bc_size])); if (likely(--pcp->bc_pincnt == 0)) { tdelete(pcp, &buf_tree, &bufctl_t_compare); PM_UNLOCK(__pmLock_libpcp); free(pcp); } else { PM_UNLOCK(__pmLock_libpcp); } return 1; }
static int __pmSecureServerNegotiation(int fd, int *strength) { PRIntervalTime timer; PRFileDesc *sslsocket; SECStatus secsts; int enabled, keysize; int msec; sslsocket = (PRFileDesc *)__pmGetSecureSocket(fd); if (!sslsocket) return PM_ERR_IPC; PM_INIT_LOCKS(); PM_LOCK(secureserver_lock); secsts = SSL_ConfigSecureServer(sslsocket, secure_server.certificate, secure_server.private_key, secure_server.certificate_KEA); PM_UNLOCK(secureserver_lock); if (secsts != SECSuccess) { pmNotifyErr(LOG_ERR, "Unable to configure secure server: %s", pmErrStr(__pmSecureSocketsError(PR_GetError()))); return PM_ERR_IPC; } secsts = SSL_ResetHandshake(sslsocket, PR_TRUE /*server*/); if (secsts != SECSuccess) { pmNotifyErr(LOG_ERR, "Unable to reset secure handshake: %s", pmErrStr(__pmSecureSocketsError(PR_GetError()))); return PM_ERR_IPC; } /* Server initiates handshake now to get early visibility of errors */ msec = __pmConvertTimeout(TIMEOUT_DEFAULT); timer = PR_MillisecondsToInterval(msec); secsts = SSL_ForceHandshakeWithTimeout(sslsocket, timer); if (secsts != SECSuccess) { pmNotifyErr(LOG_ERR, "Unable to force secure handshake: %s", pmErrStr(__pmSecureSocketsError(PR_GetError()))); return PM_ERR_IPC; } secsts = SSL_SecurityStatus(sslsocket, &enabled, NULL, &keysize, NULL, NULL, NULL); if (secsts != SECSuccess) return __pmSecureSocketsError(PR_GetError()); *strength = (enabled > 0) ? keysize : DEFAULT_SECURITY_STRENGTH; return 0; }
void __pmConnectGetPorts(pmHostSpec *host) { PM_INIT_LOCKS(); PM_LOCK(__pmLock_libpcp); load_pmcd_ports(); if (__pmAddHostPorts(host, global_portlist, global_nports) < 0) { __pmNotifyErr(LOG_WARNING, "__pmConnectGetPorts: portlist dup failed, " "using default PMCD_PORT (%d)\n", SERVER_PORT); host->ports[0] = SERVER_PORT; host->nports = 1; } PM_UNLOCK(__pmLock_libpcp); }
int __pmPtrToHandle(__pmContext *ctxp) { int i; PM_INIT_LOCKS(); PM_LOCK(__pmLock_libpcp); for (i = 0; i < contexts_len; i++) { if (ctxp == contexts[i]) { PM_UNLOCK(__pmLock_libpcp); return i; } } PM_UNLOCK(__pmLock_libpcp); return PM_CONTEXT_UNDEF; }
void __pmCountPDUBuf(int need, int *alloc, int *free) { PM_INIT_LOCKS(); PM_LOCK(__pmLock_libpcp); pdu_bufcnt_need = need; pdu_bufcnt = 0; twalk(buf_tree, &pdubufcount); *alloc = pdu_bufcnt; *free = 0; /* We don't retain freed nodes. */ PM_UNLOCK(__pmLock_libpcp); }
__pmPDU * __pmFindPDUBuf(int need) { bufctl_t *pcp; void *bcp; PM_INIT_LOCKS(); if (unlikely(need < 0)) { /* special diagnostic case ... dump buffer state */ #ifdef PCP_DEBUG fprintf(stderr, "__pmFindPDUBuf(DEBUG)\n"); pdubufdump(); #endif return NULL; } if ((pcp = (bufctl_t *)malloc(sizeof(*pcp) + need)) == NULL) { return NULL; } pcp->bc_pincnt = 1; pcp->bc_size = need; pcp->bc_buf = ((char *)pcp) + sizeof(*pcp); PM_LOCK(__pmLock_libpcp); /* Insert the node in the tree. */ bcp = tsearch((void *)pcp, &buf_tree, &bufctl_t_compare); if (unlikely(bcp == NULL)) { /* ENOMEM */ PM_UNLOCK(__pmLock_libpcp); free(pcp); return NULL; } PM_UNLOCK(__pmLock_libpcp); #ifdef PCP_DEBUG if (unlikely(pmDebug & DBG_TRACE_PDUBUF)) { fprintf(stderr, "__pmFindPDUBuf(%d) -> " PRINTF_P_PFX "%p\n", need, pcp->bc_buf); pdubufdump(); } #endif return (__pmPDU *)pcp->bc_buf; }
/* * On success, context is locked and caller should unlock it */ __pmContext * __pmHandleToPtr(int handle) { PM_INIT_LOCKS(); PM_LOCK(__pmLock_libpcp); if (handle < 0 || handle >= contexts_len || contexts[handle]->c_type == PM_CONTEXT_FREE) { PM_UNLOCK(__pmLock_libpcp); return NULL; } else { __pmContext *sts; sts = contexts[handle]; PM_UNLOCK(__pmLock_libpcp); PM_LOCK(sts->c_lock); return sts; } }
static char * pmgetconfig(const char *name, int fatal) { /* * state controls one-trip initialization, and recursion guard * for pathological failures in initialization */ static int state = 0; char *val; PM_INIT_LOCKS(); PM_LOCK(__pmLock_libpcp); if (state == 0) { state = 1; PM_UNLOCK(__pmLock_libpcp); __pmconfig(__pmNativeConfig, fatal); PM_LOCK(__pmLock_libpcp); state = 2; } else if (state == 1) { /* recursion from error in __pmConfig() ... no value is possible */ PM_UNLOCK(__pmLock_libpcp); if (pmDebug & DBG_TRACE_CONFIG) fprintf(stderr, "pmgetconfig: %s= ... recursion error\n", name); if (!fatal) return NULL; val = ""; return val; } PM_UNLOCK(__pmLock_libpcp); if ((val = getenv(name)) == NULL) { if (!fatal) return NULL; val = ""; } if (pmDebug & DBG_TRACE_CONFIG) fprintf(stderr, "pmgetconfig: %s=%s\n", name, val); return val; }
void __pmSecureServerShutdown(void) { PM_INIT_LOCKS(); PM_LOCK(secureserver_lock); if (secure_server.certificate) { CERT_DestroyCertificate(secure_server.certificate); secure_server.certificate = NULL; } if (secure_server.private_key) { SECKEY_DestroyPrivateKey(secure_server.private_key); secure_server.private_key = NULL; } if (secure_server.ssl_session_cache_setup) { SSL_ShutdownServerSessionIDCache(); secure_server.ssl_session_cache_setup = 0; } if (secure_server.initialized) { NSS_Shutdown(); secure_server.initialized = 0; } PM_UNLOCK(secureserver_lock); }
int __pmSecureServerSetup(const char *db, const char *passwd) { PM_INIT_LOCKS(); PM_LOCK(__pmLock_libpcp); /* Configure optional (cmdline) password file in case DB locked */ secure_server.password_file = passwd; /* * Configure location of the NSS database with a sane default. * For servers, we default to the shared (sql) system-wide database. * If command line db specified, pass it directly through - allowing * any old database format, at the users discretion. */ if (db) { /* shortened-buffer-size (-2) guarantees null-termination */ strncpy(secure_server.database_path, db, MAXPATHLEN-2); } PM_UNLOCK(__pmLock_libpcp); return 0; }
int pmWhichContext(void) { /* * return curcontext, provided it is defined */ int sts; PM_INIT_LOCKS(); PM_LOCK(__pmLock_libpcp); if (PM_TPD(curcontext) > PM_CONTEXT_UNDEF) sts = PM_TPD(curcontext); else sts = PM_ERR_NOCONTEXT; #ifdef PCP_DEBUG if (pmDebug & DBG_TRACE_CONTEXT) fprintf(stderr, "pmWhichContext() -> %d, cur=%d\n", sts, PM_TPD(curcontext)); #endif PM_UNLOCK(__pmLock_libpcp); return sts; }
static char * certificate_database_password(PK11SlotInfo *info, PRBool retry, void *arg) { size_t length = MAX_NSSDB_PASSWORD_LENGTH; char *password = NULL; char passfile[MAXPATHLEN]; int sts; (void)arg; (void)info; PM_INIT_LOCKS(); PM_LOCK(__pmLock_libpcp); passfile[0] = '\0'; if (secure_server.password_file) strncpy(passfile, secure_server.password_file, MAXPATHLEN-1); passfile[MAXPATHLEN-1] = '\0'; PM_UNLOCK(__pmLock_libpcp); if (passfile[0] == '\0') { __pmNotifyErr(LOG_ERR, "Password sought but no password file given"); return NULL; } if (retry) { __pmNotifyErr(LOG_ERR, "Retry attempted during password extraction"); return NULL; /* no soup^Wretries for you */ } sts = secure_file_contents(passfile, &password, &length); if (sts < 0) { __pmNotifyErr(LOG_ERR, "Cannot read password file \"%s\": %s", passfile, pmErrStr(sts)); return NULL; } return password; }
int __pmSecureServerInit(void) { const PRUint16 *cipher; SECStatus secsts; int pathSpecified; int sts = 0; PM_INIT_LOCKS(); PM_LOCK(secureserver_lock); /* Only attempt this once. */ if (secure_server.initialized) goto done; secure_server.initialized = 1; if (PR_Initialized() != PR_TRUE) PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); /* Configure optional (cmdline) password file in case DB locked */ PK11_SetPasswordFunc(certificate_database_password); /* * Configure location of the NSS database with a sane default. * For servers, we default to the shared (sql) system-wide database. * If command line db specified, pass it directly through - allowing * any old database format, at the users discretion. */ if (!secure_server.database_path[0]) { const char *path; pathSpecified = 0; path = serverdb(secure_server.database_path, MAXPATHLEN, "sql:"); /* this is the default case on some platforms, so no log spam */ if (access(path, R_OK|X_OK) < 0) { if (pmDebugOptions.context) pmNotifyErr(LOG_INFO, "Cannot access system security database: %s", secure_server.database_path); sts = -EOPNOTSUPP; /* not fatal - just no secure connections */ secure_server.init_failed = 1; goto done; } } else pathSpecified = 1; /* * pmproxy acts as both a client and server. Since the * server init path happens first, the db previously * got opened readonly. Instead try to open RW. * Fallback if there is an error. */ secsts = NSS_InitReadWrite(secure_server.database_path); if( secsts != SECSuccess ) secsts = NSS_Init(secure_server.database_path); if (secsts != SECSuccess && !pathSpecified) { /* fallback, older versions of NSS do not support sql: */ serverdb(secure_server.database_path, MAXPATHLEN, ""); secsts = NSS_Init(secure_server.database_path); } if (secsts != SECSuccess) { pmNotifyErr(LOG_ERR, "Cannot setup certificate DB (%s): %s", secure_server.database_path, pmErrStr(__pmSecureSocketsError(PR_GetError()))); sts = -EOPNOTSUPP; /* not fatal - just no secure connections */ secure_server.init_failed = 1; goto done; } /* Some NSS versions don't do this correctly in NSS_SetDomesticPolicy. */ for (cipher = SSL_GetImplementedCiphers(); *cipher != 0; ++cipher) SSL_CipherPolicySet(*cipher, SSL_ALLOWED); /* Configure SSL session cache for multi-process server, using defaults */ secsts = SSL_ConfigMPServerSIDCache(1, 0, 0, NULL); if (secsts != SECSuccess) { pmNotifyErr(LOG_ERR, "Unable to configure SSL session ID cache: %s", pmErrStr(__pmSecureSocketsError(PR_GetError()))); sts = -EOPNOTSUPP; /* not fatal - just no secure connections */ secure_server.init_failed = 1; goto done; } else { secure_server.ssl_session_cache_setup = 1; } /* * Iterate over any/all PCP Collector nickname certificates, * seeking one valid certificate. No-such-nickname is not an * error (not configured by admin at all) but anything else is. */ CERTCertList *certlist; CERTCertDBHandle *nssdb = CERT_GetDefaultCertDB(); CERTCertificate *dbcert = PK11_FindCertFromNickname(secure_server.cert_nickname, NULL); if (dbcert) { PRTime now = PR_Now(); SECItem *name = &dbcert->derSubject; CERTCertListNode *node; certlist = CERT_CreateSubjectCertList(NULL, nssdb, name, now, PR_FALSE); if (certlist) { for (node = CERT_LIST_HEAD(certlist); !CERT_LIST_END(node, certlist); node = CERT_LIST_NEXT (node)) { if (pmDebugOptions.context) __pmDumpCertificate(stderr, secure_server.cert_nickname, node->cert); if (!__pmValidCertificate(nssdb, node->cert, now)) continue; secure_server.certificate_verified = 1; break; } CERT_DestroyCertList(certlist); } if (secure_server.certificate_verified) { secure_server.certificate_KEA = NSS_FindCertKEAType(dbcert); secure_server.private_key = PK11_FindKeyByAnyCert(dbcert, NULL); if (!secure_server.private_key) { pmNotifyErr(LOG_ERR, "Unable to extract %s private key", secure_server.cert_nickname); CERT_DestroyCertificate(dbcert); secure_server.certificate_verified = 0; sts = -EOPNOTSUPP; /* not fatal - just no secure connections */ secure_server.init_failed = 1; goto done; } } else { pmNotifyErr(LOG_ERR, "Unable to find a valid %s", secure_server.cert_nickname); CERT_DestroyCertificate(dbcert); sts = -EOPNOTSUPP; /* not fatal - just no secure connections */ secure_server.init_failed = 1; goto done; } } if (! secure_server.certificate_verified) { if (pmDebugOptions.context) { pmNotifyErr(LOG_INFO, "No valid %s in security database: %s", secure_server.cert_nickname, secure_server.database_path); } sts = -EOPNOTSUPP; /* not fatal - just no secure connections */ secure_server.init_failed = 1; goto done; } secure_server.certificate = dbcert; secure_server.init_failed = 0; sts = 0; done: PM_UNLOCK(secureserver_lock); return sts; }
int __pmConnectLocal(__pmHashCtl *attrs) { int i; __pmDSO *dp; char pathbuf[MAXPATHLEN]; const char *path; #if defined(HAVE_DLOPEN) unsigned int challenge; void (*initp)(pmdaInterface *); #ifdef HAVE_ATEXIT static int atexit_installed = 0; #endif #endif if (numdso == -1) { int sts; sts = build_dsotab(); if (sts < 0) return sts; } for (i = 0; i < numdso; i++) { dp = &dsotab[i]; if (dp->domain == -1 || dp->handle != NULL) continue; /* * __pmLocalPMDA() means the path to the DSO may be something * other than relative to $PCP_PMDAS_DIR ... need to try both * options and also with and without DSO_SUFFIX (so, dll, etc) */ snprintf(pathbuf, sizeof(pathbuf), "%s%c%s", pmGetConfig("PCP_PMDAS_DIR"), __pmPathSeparator(), dp->name); if ((path = __pmFindPMDA(pathbuf)) == NULL) { snprintf(pathbuf, sizeof(pathbuf), "%s%c%s.%s", pmGetConfig("PCP_PMDAS_DIR"), __pmPathSeparator(), dp->name, DSO_SUFFIX); if ((path = __pmFindPMDA(pathbuf)) == NULL) { if ((path = __pmFindPMDA(dp->name)) == NULL) { snprintf(pathbuf, sizeof(pathbuf), "%s.%s", dp->name, DSO_SUFFIX); if ((path = __pmFindPMDA(pathbuf)) == NULL) { pmprintf("__pmConnectLocal: Warning: cannot find DSO at \"%s\" or \"%s\"\n", pathbuf, dp->name); pmflush(); dp->domain = -1; dp->handle = NULL; continue; } } } } #if defined(HAVE_DLOPEN) dp->handle = dlopen(path, RTLD_NOW); if (dp->handle == NULL) { pmprintf("__pmConnectLocal: Warning: error attaching DSO " "\"%s\"\n%s\n\n", path, dlerror()); pmflush(); dp->domain = -1; } #else /* ! HAVE_DLOPEN */ dp->handle = NULL; pmprintf("__pmConnectLocal: Warning: error attaching DSO \"%s\"\n", path); pmprintf("No dynamic DSO/DLL support on this platform\n\n"); pmflush(); dp->domain = -1; #endif if (dp->handle == NULL) continue; #if defined(HAVE_DLOPEN) /* * rest of this only makes sense if the dlopen() worked */ if (dp->init == NULL) initp = NULL; else initp = (void (*)(pmdaInterface *))dlsym(dp->handle, dp->init); if (initp == NULL) { pmprintf("__pmConnectLocal: Warning: couldn't find init function " "\"%s\" in DSO \"%s\"\n", dp->init, path); pmflush(); dlclose(dp->handle); dp->domain = -1; continue; } /* * Pass in the expected domain id. * The PMDA initialization routine can (a) ignore it, (b) check it * is the expected value, or (c) self-adapt. */ dp->dispatch.domain = dp->domain; /* * the PMDA interface / PMAPI version discovery as a "challenge" ... * for pmda_interface it is all the bits being set, * for pmapi_version it is the complement of the one you are using now */ challenge = 0xff; dp->dispatch.comm.pmda_interface = challenge; dp->dispatch.comm.pmapi_version = ~PMAPI_VERSION; dp->dispatch.comm.flags = 0; dp->dispatch.status = 0; (*initp)(&dp->dispatch); if (dp->dispatch.status != 0) { /* initialization failed for some reason */ char errmsg[PM_MAXERRMSGLEN]; pmprintf("__pmConnectLocal: Warning: initialization " "routine \"%s\" failed in DSO \"%s\": %s\n", dp->init, path, pmErrStr_r(dp->dispatch.status, errmsg, sizeof(errmsg))); pmflush(); dlclose(dp->handle); dp->domain = -1; } else { if (dp->dispatch.comm.pmda_interface < PMDA_INTERFACE_2 || dp->dispatch.comm.pmda_interface > PMDA_INTERFACE_LATEST) { pmprintf("__pmConnectLocal: Error: Unknown PMDA interface " "version %d in \"%s\" DSO\n", dp->dispatch.comm.pmda_interface, path); pmflush(); dlclose(dp->handle); dp->domain = -1; } else if (dp->dispatch.comm.pmapi_version != PMAPI_VERSION_2) { pmprintf("__pmConnectLocal: Error: Unknown PMAPI version %d " "in \"%s\" DSO\n", dp->dispatch.comm.pmapi_version, path); pmflush(); dlclose(dp->handle); dp->domain = -1; } else if (dp->dispatch.comm.pmda_interface >= PMDA_INTERFACE_6 && (dp->dispatch.comm.flags & PDU_FLAG_AUTH) != 0) { /* Agent wants to know about connection attributes */ build_dsoattrs(&dp->dispatch, attrs); } } #ifdef HAVE_ATEXIT PM_INIT_LOCKS(); PM_LOCK(__pmLock_libpcp); if (dp->dispatch.comm.pmda_interface >= PMDA_INTERFACE_5 && atexit_installed == 0) { /* install end of local context handler */ atexit(EndLocalContext); atexit_installed = 1; } PM_UNLOCK(__pmLock_libpcp); #endif #endif /* HAVE_DLOPEN */ } return 0; }
/* result is pinned on successful return */ int __pmGetPDU(int fd, int mode, int timeout, __pmPDU **result) { int need; int len; static int maxsize = PDU_CHUNK; char *handle; __pmPDU *pdubuf; __pmPDU *pdubuf_prev; __pmPDUHdr *php; if ((pdubuf = __pmFindPDUBuf(maxsize)) == NULL) return -oserror(); /* First read - try to read the header */ len = pduread(fd, (void *)pdubuf, sizeof(__pmPDUHdr), HEADER, timeout); php = (__pmPDUHdr *)pdubuf; if (len < (int)sizeof(__pmPDUHdr)) { if (len == -1) { if (__pmSocketClosed()) { len = 0; } else { char errmsg[PM_MAXERRMSGLEN]; __pmNotifyErr(LOG_ERR, "__pmGetPDU: fd=%d hdr read: len=%d: %s", fd, len, pmErrStr_r(-oserror(), errmsg, sizeof(errmsg))); } } else if (len >= (int)sizeof(php->len)) { /* * Have part of a PDU header. Enough for the "len" * field to be valid, but not yet all of it - save * what we have received and try to read some more. * Note this can only happen once per PDU, so the * ntohl() below will _only_ be done once per PDU. */ goto check_read_len; /* continue, do not return */ } else if (len == PM_ERR_TIMEOUT) { __pmUnpinPDUBuf(pdubuf); return PM_ERR_TIMEOUT; } else if (len < 0) { char errmsg[PM_MAXERRMSGLEN]; __pmNotifyErr(LOG_ERR, "__pmGetPDU: fd=%d hdr read: len=%d: %s", fd, len, pmErrStr_r(len, errmsg, sizeof(errmsg))); __pmUnpinPDUBuf(pdubuf); return PM_ERR_IPC; } else if (len > 0) { __pmNotifyErr(LOG_ERR, "__pmGetPDU: fd=%d hdr read: bad len=%d", fd, len); __pmUnpinPDUBuf(pdubuf); return PM_ERR_IPC; } /* * end-of-file with no data */ __pmUnpinPDUBuf(pdubuf); return 0; } check_read_len: php->len = ntohl(php->len); if (php->len < (int)sizeof(__pmPDUHdr)) { /* * PDU length indicates insufficient bytes for a PDU header * ... looks like DOS attack like PV 935490 */ __pmNotifyErr(LOG_ERR, "__pmGetPDU: fd=%d illegal PDU len=%d in hdr", fd, php->len); __pmUnpinPDUBuf(pdubuf); return PM_ERR_IPC; } else if (mode == LIMIT_SIZE && php->len > ceiling) { /* * Guard against denial of service attack ... don't accept PDUs * from clients that are larger than 64 Kbytes (ceiling) * (note, pmcd and pmdas have to be able to _send_ large PDUs, * e.g. for a pmResult or instance domain enquiry) */ __pmNotifyErr(LOG_ERR, "__pmGetPDU: fd=%d bad PDU len=%d in hdr exceeds maximum client PDU size (%d)", fd, php->len, ceiling); __pmUnpinPDUBuf(pdubuf); return PM_ERR_TOOBIG; } if (len < php->len) { /* * need to read more ... */ int tmpsize; int have = len; PM_INIT_LOCKS(); PM_LOCK(__pmLock_libpcp); if (php->len > maxsize) { tmpsize = PDU_CHUNK * ( 1 + php->len / PDU_CHUNK); maxsize = tmpsize; } else tmpsize = maxsize; PM_UNLOCK(__pmLock_libpcp); pdubuf_prev = pdubuf; if ((pdubuf = __pmFindPDUBuf(tmpsize)) == NULL) { __pmUnpinPDUBuf(pdubuf_prev); return -oserror(); } memmove((void *)pdubuf, (void *)php, len); __pmUnpinPDUBuf(pdubuf_prev); php = (__pmPDUHdr *)pdubuf; need = php->len - have; handle = (char *)pdubuf; /* block until all of the PDU is received this time */ len = pduread(fd, (void *)&handle[len], need, BODY, timeout); if (len != need) { if (len == PM_ERR_TIMEOUT) { __pmUnpinPDUBuf(pdubuf); return PM_ERR_TIMEOUT; } else if (len < 0) { char errmsg[PM_MAXERRMSGLEN]; __pmNotifyErr(LOG_ERR, "__pmGetPDU: fd=%d data read: len=%d: %s", fd, len, pmErrStr_r(-oserror(), errmsg, sizeof(errmsg))); } else __pmNotifyErr(LOG_ERR, "__pmGetPDU: fd=%d data read: have %d, want %d, got %d", fd, have, need, len); /* * only report header fields if you've read enough bytes */ if (len > 0) have += len; if (have >= (int)(sizeof(php->len)+sizeof(php->type)+sizeof(php->from))) __pmNotifyErr(LOG_ERR, "__pmGetPDU: PDU hdr: len=0x%x type=0x%x from=0x%x", php->len, (unsigned)ntohl(php->type), (unsigned)ntohl(php->from)); else if (have >= (int)(sizeof(php->len)+sizeof(php->type))) __pmNotifyErr(LOG_ERR, "__pmGetPDU: PDU hdr: len=0x%x type=0x%x", php->len, (unsigned)ntohl(php->type)); __pmUnpinPDUBuf(pdubuf); return PM_ERR_IPC; } } *result = (__pmPDU *)php; php->type = ntohl((unsigned int)php->type); if (php->type < 0) { /* * PDU type is bad ... could be a possible mem leak attack like * https://bugzilla.redhat.com/show_bug.cgi?id=841319 */ __pmNotifyErr(LOG_ERR, "__pmGetPDU: fd=%d illegal PDU type=%d in hdr", fd, php->type); __pmUnpinPDUBuf(pdubuf); return PM_ERR_IPC; } php->from = ntohl((unsigned int)php->from); #ifdef PCP_DEBUG if (pmDebug & DBG_TRACE_PDU) { int j; char *p; int jend = PM_PDU_SIZE(php->len); char strbuf[20]; /* clear the padding bytes, lest they contain garbage */ p = (char *)*result + php->len; while (p < (char *)*result + jend*sizeof(__pmPDU)) *p++ = '~'; /* buffer end */ if (mypid == -1) mypid = (int)getpid(); fprintf(stderr, "[%d]pmGetPDU: %s fd=%d len=%d from=%d", mypid, __pmPDUTypeStr_r(php->type, strbuf, sizeof(strbuf)), fd, php->len, php->from); for (j = 0; j < jend; j++) { if ((j % 8) == 0) fprintf(stderr, "\n%03d: ", j); fprintf(stderr, "%8x ", (*result)[j]); } putc('\n', stderr); } #endif if (php->type >= PDU_START && php->type <= PDU_FINISH) __pmPDUCntIn[php->type-PDU_START]++; /* * Note php points into the PDU buffer pdubuf that remains pinned * and php is returned via the result parameter ... see the * thread-safe comments above */ return php->type; }
int __pmConnectPMCD(pmHostSpec *hosts, int nhosts, int ctxflags, __pmHashCtl *attrs) { int sts = -1; int fd = -1; /* Fd for socket connection to pmcd */ int *ports; int nports; int portIx; int version = -1; int proxyport; pmHostSpec *proxyhost; static int first_time = 1; static pmHostSpec proxy; PM_INIT_LOCKS(); PM_LOCK(__pmLock_libpcp); if (first_time) { /* * One-trip check for use of pmproxy(1) in lieu of pmcd(1), * and to extract the optional environment variables ... * PMCD_PORT, PMPROXY_HOST and PMPROXY_PORT. * We also check for the presense of a certificate database * and load it up if either a user or system (global) DB is * found. */ first_time = 0; load_pmcd_ports(); load_proxy_hostspec(&proxy); } if (hosts[0].nports == 0) { nports = global_nports; ports = global_portlist; } else { nports = hosts[0].nports; ports = hosts[0].ports; } if (proxy.name == NULL && nhosts == 1) { const char *name = (const char *)hosts[0].name; /* * no proxy, connecting directly to pmcd */ PM_UNLOCK(__pmLock_libpcp); sts = -1; /* Try connecting via the local unix domain socket, if requested and supported. */ if (nports == PM_HOST_SPEC_NPORTS_LOCAL || nports == PM_HOST_SPEC_NPORTS_UNIX) { #if defined(HAVE_STRUCT_SOCKADDR_UN) if ((fd = __pmAuxConnectPMCDUnixSocket(name)) >= 0) { if ((sts = __pmConnectHandshake(fd, name, ctxflags, attrs)) < 0) { __pmCloseSocket(fd); } else sts = fd; portIx = -1; /* no port */ } #endif /* * If the connection failed, or is not supported, and the protocol was 'local:', * then try connecting to localhost via the default port(s). */ if (sts < 0) { if (nports == PM_HOST_SPEC_NPORTS_LOCAL) { name = "localhost"; nports = global_nports; ports = global_portlist; sts = -1; /* keep trying */ } else sts = -2; /* no more connection attempts. */ } } /* If still not connected, try via the given host name and ports, if requested. */ if (sts == -1) { for (portIx = 0; portIx < nports; portIx++) { if ((fd = __pmAuxConnectPMCDPort(name, ports[portIx])) >= 0) { if ((sts = __pmConnectHandshake(fd, name, ctxflags, attrs)) < 0) { __pmCloseSocket(fd); } else /* success */ break; } else sts = fd; } } if (sts < 0) { #ifdef PCP_DEBUG if (pmDebug & DBG_TRACE_CONTEXT) { char errmsg[PM_MAXERRMSGLEN]; fprintf(stderr, "__pmConnectPMCD(%s): pmcd connection port=", hosts[0].name); for (portIx = 0; portIx < nports; portIx++) { if (portIx == 0) fprintf(stderr, "%d", ports[portIx]); else fprintf(stderr, ",%d", ports[portIx]); } fprintf(stderr, " failed: %s\n", pmErrStr_r(sts, errmsg, sizeof(errmsg))); } #endif return sts; } #ifdef PCP_DEBUG if (pmDebug & DBG_TRACE_CONTEXT) { if (portIx >= 0) { fprintf(stderr, "__pmConnectPMCD(%s): pmcd connection port=%d fd=%d PDU version=%u\n", hosts[0].name, ports[portIx], fd, __pmVersionIPC(fd)); } else { fprintf(stderr, "__pmConnectPMCD(%s): pmcd connection path=%s fd=%d PDU version=%u\n", hosts[0].name, name, fd, __pmVersionIPC(fd)); } __pmPrintIPC(); } #endif return fd; } /* * connecting to pmproxy, and then to pmcd ... not a direct * connection to pmcd */ proxyhost = (nhosts > 1) ? &hosts[1] : &proxy; proxyport = (proxyhost->nports > 0) ? proxyhost->ports[0] : PROXY_PORT; for (portIx = 0; portIx < nports; portIx++) { fd = __pmAuxConnectPMCDPort(proxyhost->name, proxyport); if (fd < 0) { #ifdef PCP_DEBUG if (pmDebug & DBG_TRACE_CONTEXT) { char errmsg[PM_MAXERRMSGLEN]; fprintf(stderr, "__pmConnectPMCD(%s): proxy to %s port=%d failed: %s \n", hosts[0].name, proxyhost->name, proxyport, pmErrStr_r(-neterror(), errmsg, sizeof(errmsg))); } #endif PM_UNLOCK(__pmLock_libpcp); return fd; } if ((sts = version = negotiate_proxy(fd, hosts[0].name, ports[portIx])) < 0) __pmCloseSocket(fd); else if ((sts = __pmConnectHandshake(fd, proxyhost->name, ctxflags, attrs)) < 0) __pmCloseSocket(fd); else /* success */ break; } if (sts < 0) { #ifdef PCP_DEBUG if (pmDebug & DBG_TRACE_CONTEXT) { char errmsg[PM_MAXERRMSGLEN]; fprintf(stderr, "__pmConnectPMCD(%s): proxy connection to %s port=", hosts[0].name, proxyhost->name); for (portIx = 0; portIx < nports; portIx++) { if (portIx == 0) fprintf(stderr, "%d", ports[portIx]); else fprintf(stderr, ",%d", ports[portIx]); } fprintf(stderr, " failed: %s\n", pmErrStr_r(sts, errmsg, sizeof(errmsg))); } #endif PM_UNLOCK(__pmLock_libpcp); return sts; } #ifdef PCP_DEBUG if (pmDebug & DBG_TRACE_CONTEXT) { fprintf(stderr, "__pmConnectPMCD(%s): proxy connection host=%s port=%d fd=%d version=%d\n", hosts[0].name, proxyhost->name, ports[portIx], fd, version); } #endif PM_UNLOCK(__pmLock_libpcp); return fd; }