static Source * CreateRemoteSource(const char *path, TupleDesc desc) { RemoteSource *self = (RemoteSource *) palloc0(sizeof(RemoteSource)); self->base.close = (SourceCloseProc) RemoteSourceClose; if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3) { /* new way */ StringInfoData buf; int16 format; int nattrs; int i; self->base.read = (SourceReadProc) RemoteSourceRead; /* count valid fields */ for (nattrs = 0, i = 0; i < desc->natts; i++) { if (desc->attrs[i]->attisdropped) continue; nattrs++; } format = (IsBinaryCopy() ? 1 : 0); pq_beginmessage(&buf, 'G'); pq_sendbyte(&buf, format); /* overall format */ pq_sendint(&buf, nattrs, 2); for (i = 0; i < nattrs; i++) pq_sendint(&buf, format, 2); /* per-column formats */ pq_endmessage(&buf); self->buffer = makeStringInfo(); } else if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2) { self->base.read = (SourceReadProc) RemoteSourceReadOld; /* old way */ if (IsBinaryCopy()) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("COPY BINARY is not supported to stdout or from stdin"))); pq_putemptymessage('G'); } else { self->base.read = (SourceReadProc) RemoteSourceReadOld; /* very old way */ if (IsBinaryCopy()) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("COPY BINARY is not supported to stdout or from stdin"))); pq_putemptymessage('D'); } /* We *must* flush here to ensure FE knows it can send. */ pq_flush(); return (Source *) self; }
/* ---------------- * ReadyForQuery - tell dest that we are ready for a new query * * The ReadyForQuery message is sent in protocol versions 2.0 and up * so that the FE can tell when we are done processing a query string. * In versions 3.0 and up, it also carries a transaction state indicator. * * Note that by flushing the stdio buffer here, we can avoid doing it * most other places and thus reduce the number of separate packets sent. * ---------------- */ void ReadyForQuery(CommandDest dest) { switch (dest) { case DestRemote: case DestRemoteExecute: if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3) { StringInfoData buf; pq_beginmessage(&buf, 'Z'); pq_sendbyte(&buf, TransactionBlockStatusCode()); pq_endmessage(&buf); } else if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2) pq_putemptymessage('Z'); /* Flush output at end of cycle in any case. */ pq_flush(); break; case DestNone: case DestDebug: case DestSPI: case DestTuplestore: case DestIntoRel: case DestCopyOut: break; } }
static void SendResultDescriptionMessage(AttributeDefinition *attrs, int natts) { int proto = PG_PROTOCOL_MAJOR(FrontendProtocol); int i; StringInfoData buf; pq_beginmessage(&buf, 'T'); /* tuple descriptor message type */ pq_sendint(&buf, natts, 2); /* # of attrs in tuples */ for (i = 0; i < natts; ++i) { pq_sendstring(&buf, attrs[i].name); /* column ID info appears in protocol 3.0 and up */ if (proto >= 3) { pq_sendint(&buf, 0, 4); pq_sendint(&buf, 0, 2); } /* If column is a domain, send the base type and typmod instead */ pq_sendint(&buf, attrs[i].typid, sizeof(Oid)); pq_sendint(&buf, attrs[i].typlen, sizeof(int16)); /* typmod appears in protocol 2.0 and up */ if (proto >= 2) pq_sendint(&buf, attrs[i].typmod, sizeof(int32)); /* format info appears in protocol 3.0 and up */ if (proto >= 3) pq_sendint(&buf, 0, 2); } pq_endmessage(&buf); }
/* * Send NOTIFY message to my front end. */ static void NotifyMyFrontEnd(char *relname, int32 listenerPID) { if (whereToSendOutput == DestRemote) { StringInfoData buf; pq_beginmessage(&buf, 'A'); pq_sendint(&buf, listenerPID, sizeof(int32)); pq_sendstring(&buf, relname); if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3) { /* XXX Add parameter string here later */ pq_sendstring(&buf, ""); } pq_endmessage(&buf); /* * NOTE: we do not do pq_flush() here. For a self-notify, it will * happen at the end of the transaction, and for incoming notifies * ProcessIncomingNotify will do it after finding all the notifies. */ } else elog(INFO, "NOTIFY for %s", relname); }
/* -------------------------------- * pq_putmessage - send a normal message (suppressed in COPY OUT mode) * * If msgtype is not '\0', it is a message type code to place before * the message body. If msgtype is '\0', then the message has no type * code (this is only valid in pre-3.0 protocols). * * len is the length of the message body data at *s. In protocol 3.0 * and later, a message length word (equal to len+4 because it counts * itself too) is inserted by this routine. * * All normal messages are suppressed while old-style COPY OUT is in * progress. (In practice only a few notice messages might get emitted * then; dropping them is annoying, but at least they will still appear * in the postmaster log.) * * We also suppress messages generated while pqcomm.c is busy. This * avoids any possibility of messages being inserted within other * messages. The only known trouble case arises if SIGQUIT occurs * during a pqcomm.c routine --- quickdie() will try to send a warning * message, and the most reasonable approach seems to be to drop it. * * returns 0 if OK, EOF if trouble * -------------------------------- */ int pq_putmessage(char msgtype, const char *s, size_t len) { if (DoingCopyOut || PqCommBusy) return 0; PqCommBusy = true; if (msgtype) if (internal_putbytes(&msgtype, 1)) goto fail; if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3) { uint32 n32; n32 = htonl((uint32) (len + 4)); if (internal_putbytes((char *) &n32, 4)) goto fail; } if (internal_putbytes(s, len)) goto fail; PqCommBusy = false; return 0; fail: PqCommBusy = false; return EOF; }
/* ---------------- * NullCommand - tell dest that an empty query string was recognized * * In FE/BE protocol version 1.0, this hack is necessary to support * libpq's crufty way of determining whether a multiple-command * query string is done. In protocol 2.0 it's probably not really * necessary to distinguish empty queries anymore, but we still do it * for backwards compatibility with 1.0. In protocol 3.0 it has some * use again, since it ensures that there will be a recognizable end * to the response to an Execute message. * ---------------- */ void NullCommand(CommandDest dest) { switch (dest) { case DestRemote: case DestRemoteExecute: /* * tell the fe that we saw an empty query string. In protocols * before 3.0 this has a useless empty-string message body. */ if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3) pq_putemptymessage('I'); else pq_puttextmessage('I', ""); break; case DestNone: case DestDebug: case DestSPI: case DestTuplestore: case DestIntoRel: case DestCopyOut: break; } }
/* * SendRowDescriptionMessage --- send a RowDescription message to the frontend * * Notes: the TupleDesc has typically been manufactured by ExecTypeFromTL() * or some similar function; it does not contain a full set of fields. * The targetlist will be NIL when executing a utility function that does * not have a plan. If the targetlist isn't NIL then it is a Query node's * targetlist; it is up to us to ignore resjunk columns in it. The formats[] * array pointer might be NULL (if we are doing Describe on a prepared stmt); * send zeroes for the format codes in that case. */ void SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist, int16 *formats) { Form_pg_attribute *attrs = typeinfo->attrs; int natts = typeinfo->natts; int proto = PG_PROTOCOL_MAJOR(FrontendProtocol); int i; StringInfoData buf; ListCell *tlist_item = list_head(targetlist); pq_beginmessage(&buf, 'T'); /* tuple descriptor message type */ pq_sendint(&buf, natts, 2); /* # of attrs in tuples */ for (i = 0; i < natts; ++i) { Oid atttypid = attrs[i]->atttypid; int32 atttypmod = attrs[i]->atttypmod; pq_sendstring(&buf, NameStr(attrs[i]->attname)); /* column ID info appears in protocol 3.0 and up */ if (proto >= 3) { /* Do we have a non-resjunk tlist item? */ while (tlist_item && ((TargetEntry *) lfirst(tlist_item))->resjunk) tlist_item = lnext(tlist_item); if (tlist_item) { TargetEntry *tle = (TargetEntry *) lfirst(tlist_item); pq_sendint(&buf, tle->resorigtbl, 4); pq_sendint(&buf, tle->resorigcol, 2); tlist_item = lnext(tlist_item); } else { /* No info available, so send zeroes */ pq_sendint(&buf, 0, 4); pq_sendint(&buf, 0, 2); } } /* If column is a domain, send the base type and typmod instead */ atttypid = getBaseTypeAndTypmod(atttypid, &atttypmod); pq_sendint(&buf, (int) atttypid, sizeof(atttypid)); pq_sendint(&buf, attrs[i]->attlen, sizeof(attrs[i]->attlen)); /* typmod appears in protocol 2.0 and up */ if (proto >= 2) pq_sendint(&buf, atttypmod, sizeof(atttypmod)); /* format info appears in protocol 3.0 and up */ if (proto >= 3) { if (formats) pq_sendint(&buf, formats[i], 2); else pq_sendint(&buf, 0, 2); } } pq_endmessage(&buf); }
static int pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq) { int ret; char *crypt_pwd = NULL; const char *pwd_to_send; /* Encrypt the password if needed. */ switch (areq) { case AUTH_REQ_MD5: { char *crypt_pwd2; /* Allocate enough space for two MD5 hashes */ crypt_pwd = malloc(2 * (MD5_PASSWD_LEN + 1)); if (!crypt_pwd) { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); return STATUS_ERROR; } crypt_pwd2 = crypt_pwd + MD5_PASSWD_LEN + 1; if (!pg_md5_encrypt(password, conn->pguser, strlen(conn->pguser), crypt_pwd2)) { free(crypt_pwd); return STATUS_ERROR; } if (!pg_md5_encrypt(crypt_pwd2 + strlen("md5"), conn->md5Salt, sizeof(conn->md5Salt), crypt_pwd)) { free(crypt_pwd); return STATUS_ERROR; } pwd_to_send = crypt_pwd; break; } case AUTH_REQ_PASSWORD: pwd_to_send = password; break; default: return STATUS_ERROR; } /* Packet has a message type as of protocol 3.0 */ if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) ret = pqPacketSend(conn, 'p', pwd_to_send, strlen(pwd_to_send) + 1); else ret = pqPacketSend(conn, 0, pwd_to_send, strlen(pwd_to_send) + 1); if (crypt_pwd) free(crypt_pwd); return ret; }
static void printtup_startup(DestReceiver *self, int operation, TupleDesc typeinfo) { DR_printtup *myState = (DR_printtup *) self; Portal portal = myState->portal; /* create buffer to be used for all messages */ initStringInfo(&myState->buf); /* * Create a temporary memory context that we can reset once per row to * recover palloc'd memory. This avoids any problems with leaks inside * datatype output routines, and should be faster than retail pfree's * anyway. */ myState->tmpcontext = AllocSetContextCreate(CurrentMemoryContext, "printtup", ALLOCSET_DEFAULT_SIZES); if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3) { /* * Send portal name to frontend (obsolete cruft, gone in proto 3.0) * * If portal name not specified, use "blank" portal. */ const char *portalName = portal->name; if (portalName == NULL || portalName[0] == '\0') portalName = "blank"; pq_puttextmessage('P', portalName); } /* * If we are supposed to emit row descriptions, then send the tuple * descriptor of the tuples. */ if (myState->sendDescrip) SendRowDescriptionMessage(&myState->buf, typeinfo, FetchPortalTargetList(portal), portal->formats); /* ---------------- * We could set up the derived attr info at this time, but we postpone it * until the first call of printtup, for 2 reasons: * 1. We don't waste time (compared to the old way) if there are no * tuples at all to output. * 2. Checking in printtup allows us to handle the case that the tuples * change type midway through (although this probably can't happen in * the current executor). * ---------------- */ }
/* -------------------------------- * pq_putmessage - send a normal message (suppressed in COPY OUT mode) * * If msgtype is not '\0', it is a message type code to place before * the message body. If msgtype is '\0', then the message has no type * code (this is only valid in pre-3.0 protocols). * * len is the length of the message body data at *s. In protocol 3.0 * and later, a message length word (equal to len+4 because it counts * itself too) is inserted by this routine. * * All normal messages are suppressed while old-style COPY OUT is in * progress. (In practice only a few notice messages might get emitted * then; dropping them is annoying, but at least they will still appear * in the postmaster log.) * * We also suppress messages generated while pqcomm.c is busy. This * avoids any possibility of messages being inserted within other * messages. * * returns 0 if OK, EOF if trouble * -------------------------------- */ int pq_putmessage(char msgtype, const char *s, size_t len) { if (DoingCopyOut) { return EOF; } if ((Gp_role == GP_ROLE_DISPATCH || Gp_role == GP_ROLE_DISPATCHAGENT) && IsUnderPostmaster) { if (!pq_send_mutex_lock()) { return EOF; } } if (msgtype) { if (internal_putbytes(&msgtype, 1)) goto fail; } if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3) { uint32 n32; n32 = htonl((uint32) (len + 4)); if (internal_putbytes((char *) &n32, 4)) goto fail; } if (internal_putbytes(s, len)) goto fail; if ((Gp_role == GP_ROLE_DISPATCH || Gp_role == GP_ROLE_DISPATCHAGENT) && IsUnderPostmaster) pthread_mutex_unlock(&send_mutex); return 0; fail: if ((Gp_role == GP_ROLE_DISPATCH || Gp_role == GP_ROLE_DISPATCHAGENT) && IsUnderPostmaster) pthread_mutex_unlock(&send_mutex); return EOF; }
/* * SendRowDescriptionMessage --- send a RowDescription message to the frontend * * Notes: the TupleDesc has typically been manufactured by ExecTypeFromTL() * or some similar function; it does not contain a full set of fields. * The targetlist will be NIL when executing a utility function that does * not have a plan. If the targetlist isn't NIL then it is a Query node's * targetlist; it is up to us to ignore resjunk columns in it. The formats[] * array pointer might be NULL (if we are doing Describe on a prepared stmt); * send zeroes for the format codes in that case. */ void SendRowDescriptionMessage(StringInfo buf, TupleDesc typeinfo, List *targetlist, int16 *formats) { int natts = typeinfo->natts; int proto = PG_PROTOCOL_MAJOR(FrontendProtocol); /* tuple descriptor message type */ pq_beginmessage_reuse(buf, 'T'); /* # of attrs in tuples */ pq_sendint16(buf, natts); if (proto >= 3) SendRowDescriptionCols_3(buf, typeinfo, targetlist, formats); else SendRowDescriptionCols_2(buf, typeinfo, targetlist, formats); pq_endmessage_reuse(buf); }
static void printtup_startup(DestReceiver *self, int operation, TupleDesc typeinfo) { DR_printtup *myState = (DR_printtup *) self; Portal portal = myState->portal; if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3) { /* * Send portal name to frontend (obsolete cruft, gone in proto 3.0) * * If portal name not specified, use "blank" portal. */ const char *portalName = portal->name; if (portalName == NULL || portalName[0] == '\0') portalName = "blank"; pq_puttextmessage('P', portalName); } /* * If we are supposed to emit row descriptions, then send the tuple * descriptor of the tuples. */ if (myState->sendDescrip) SendRowDescriptionMessage(typeinfo, FetchPortalTargetList(portal), portal->formats); /* ---------------- * We could set up the derived attr info at this time, but we postpone it * until the first call of printtup, for 2 reasons: * 1. We don't waste time (compared to the old way) if there are no * tuples at all to output. * 2. Checking in printtup allows us to handle the case that the tuples * change type midway through (although this probably can't happen in * the current executor). * ---------------- */ }
static void send_buffer() { if (buffer_len > 0) { StringInfoData msgbuf; char *cursor = buffer; while (--buffer_len > 0) { if (*cursor == '\0') *cursor = '\n'; cursor++; } if (*cursor != '\0') ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("internal error"), errdetail("Wrong message format detected"))); pq_beginmessage(&msgbuf, 'N'); if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3) { pq_sendbyte(&msgbuf, PG_DIAG_MESSAGE_PRIMARY); pq_sendstring(&msgbuf, buffer); pq_sendbyte(&msgbuf, '\0'); } else { *cursor++ = '\n'; *cursor = '\0'; pq_sendstring(&msgbuf, buffer); } pq_endmessage(&msgbuf); pq_flush(); } }
/* * pqPutMsgStart: begin construction of a message to the server * * msg_type is the message type byte, or 0 for a message without type byte * (only startup messages have no type byte) * * force_len forces the message to have a length word; otherwise, we add * a length word if protocol 3. * * Returns 0 on success, EOF on error * * The idea here is that we construct the message in conn->outBuffer, * beginning just past any data already in outBuffer (ie, at * outBuffer+outCount). We enlarge the buffer as needed to hold the message. * When the message is complete, we fill in the length word (if needed) and * then advance outCount past the message, making it eligible to send. * * The state variable conn->outMsgStart points to the incomplete message's * length word: it is either outCount or outCount+1 depending on whether * there is a type byte. If we are sending a message without length word * (pre protocol 3.0 only), then outMsgStart is -1. The state variable * conn->outMsgEnd is the end of the data collected so far. */ int pqPutMsgStart(char msg_type, bool force_len, PGconn *conn) { int lenPos; int endPos; /* allow room for message type byte */ if (msg_type) endPos = conn->outCount + 1; else endPos = conn->outCount; /* do we want a length word? */ if (force_len || PG_PROTOCOL_MAJOR(conn->pversion) >= 3) { lenPos = endPos; /* allow room for message length */ endPos += 4; } else lenPos = -1; /* make sure there is room for message header */ if (pqCheckOutBufferSpace(endPos, conn)) return EOF; /* okay, save the message type byte if any */ if (msg_type) conn->outBuffer[conn->outCount] = msg_type; /* set up the message pointers */ conn->outMsgStart = lenPos; conn->outMsgEnd = endPos; /* length word, if needed, will be filled in by pqPutMsgEnd */ if (conn->Pfdebug) fprintf(conn->Pfdebug, "To backend> Msg %c\n", msg_type ? msg_type : ' '); return 0; }
/* ---------------- * Initialize: create a DestReceiver for printtup * ---------------- */ DestReceiver * printtup_create_DR(CommandDest dest, Portal portal) { DR_printtup *self = (DR_printtup *) palloc(sizeof(DR_printtup)); if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3) self->pub.receiveSlot = printtup; else { /* * In protocol 2.0 the Bind message does not exist, so there is no way * for the columns to have different print formats; it's sufficient to * look at the first one. */ if (portal->formats && portal->formats[0] != 0) self->pub.receiveSlot = printtup_internal_20; else self->pub.receiveSlot = printtup_20; } self->pub.rStartup = printtup_startup; self->pub.rShutdown = printtup_shutdown; self->pub.rDestroy = printtup_destroy; self->pub.mydest = dest; self->portal = portal; /* * Send T message automatically if DestRemote, but not if * DestRemoteExecute */ self->sendDescrip = (dest == DestRemote); self->attrinfo = NULL; self->nattrs = 0; self->myinfo = NULL; return (DestReceiver *) self; }
/* * Set parameters for a DestRemote (or DestRemoteExecute) receiver */ void SetRemoteDestReceiverParams(DestReceiver *self, Portal portal) { DR_printtup *myState = (DR_printtup *) self; Assert(myState->pub.mydest == DestRemote || myState->pub.mydest == DestRemoteExecute); myState->portal = portal; if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3) { /* * In protocol 2.0 the Bind message does not exist, so there is no way * for the columns to have different print formats; it's sufficient to * look at the first one. */ if (portal->formats && portal->formats[0] != 0) myState->pub.receiveSlot = printtup_internal_20; else myState->pub.receiveSlot = printtup_20; } }
/* * Collect password response packet from frontend. * * Returns NULL if couldn't get password, else palloc'd string. */ static char * recv_password_packet(Port *port) { StringInfoData buf; if (PG_PROTOCOL_MAJOR(port->proto) >= 3) { /* Expect 'p' message type */ int mtype; mtype = pq_getbyte(); if (mtype != 'p') { /* * If the client just disconnects without offering a password, * don't make a log entry. This is legal per protocol spec * and in fact commonly done by psql, so complaining just * clutters the log. */ if (mtype != EOF) ereport(COMMERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("expected password response, got message type %d", mtype))); return NULL; /* EOF or bad message type */ } } else { /* For pre-3.0 clients, avoid log entry if they just disconnect */ if (pq_peekbyte() == EOF) return NULL; /* EOF */ } initStringInfo(&buf); if (pq_getmessage(&buf, 1000)) /* receive password */ { /* EOF - pq_getmessage already logged a suitable message */ pfree(buf.data); return NULL; } /* * Apply sanity check: password packet length should agree with length * of contained string. Note it is safe to use strlen here because * StringInfo is guaranteed to have an appended '\0'. */ if (strlen(buf.data) + 1 != buf.len) ereport(COMMERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("invalid password packet size"))); /* Do not echo password to logs, for security. */ ereport(DEBUG5, (errmsg("received password packet"))); /* * Return the received string. Note we do not attempt to do any * character-set conversion on it; since we don't yet know the * client's encoding, there wouldn't be much point. */ return buf.data; }
static int pg_SSPI_recvauth(Port *port) { int mtype; StringInfoData buf; SECURITY_STATUS r; CredHandle sspicred; CtxtHandle *sspictx = NULL, newctx; TimeStamp expiry; ULONG contextattr; SecBufferDesc inbuf; SecBufferDesc outbuf; SecBuffer OutBuffers[1]; SecBuffer InBuffers[1]; HANDLE token; TOKEN_USER *tokenuser; DWORD retlen; char accountname[MAXPGPATH]; char domainname[MAXPGPATH]; DWORD accountnamesize = sizeof(accountname); DWORD domainnamesize = sizeof(domainname); SID_NAME_USE accountnameuse; HMODULE secur32; QUERY_SECURITY_CONTEXT_TOKEN_FN _QuerySecurityContextToken; /* * SSPI auth is not supported for protocol versions before 3, because it * relies on the overall message length word to determine the SSPI payload * size in AuthenticationGSSContinue and PasswordMessage messages. * (This is, in fact, a design error in our SSPI support, because protocol * messages are supposed to be parsable without relying on the length * word; but it's not worth changing it now.) */ if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3) ereport(FATAL, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("SSPI is not supported in protocol version 2"))); /* * Acquire a handle to the server credentials. */ r = AcquireCredentialsHandle(NULL, "negotiate", SECPKG_CRED_INBOUND, NULL, NULL, NULL, NULL, &sspicred, &expiry); if (r != SEC_E_OK) pg_SSPI_error(ERROR, gettext_noop("could not acquire SSPI credentials handle"), r); /* * Loop through SSPI message exchange. This exchange can consist of * multiple messags sent in both directions. First message is always from * the client. All messages from client to server are password packets * (type 'p'). */ do { mtype = pq_getbyte(); if (mtype != 'p') { /* Only log error if client didn't disconnect. */ if (mtype != EOF) ereport(COMMERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("expected SSPI response, got message type %d", mtype))); return STATUS_ERROR; } /* Get the actual SSPI token */ initStringInfo(&buf); if (pq_getmessage(&buf, 2000)) { /* EOF - pq_getmessage already logged error */ pfree(buf.data); return STATUS_ERROR; } /* Map to SSPI style buffer */ inbuf.ulVersion = SECBUFFER_VERSION; inbuf.cBuffers = 1; inbuf.pBuffers = InBuffers; InBuffers[0].pvBuffer = buf.data; InBuffers[0].cbBuffer = buf.len; InBuffers[0].BufferType = SECBUFFER_TOKEN; /* Prepare output buffer */ OutBuffers[0].pvBuffer = NULL; OutBuffers[0].BufferType = SECBUFFER_TOKEN; OutBuffers[0].cbBuffer = 0; outbuf.cBuffers = 1; outbuf.pBuffers = OutBuffers; outbuf.ulVersion = SECBUFFER_VERSION; elog(DEBUG4, "Processing received SSPI token of length %u", (unsigned int) buf.len); r = AcceptSecurityContext(&sspicred, sspictx, &inbuf, ASC_REQ_ALLOCATE_MEMORY, SECURITY_NETWORK_DREP, &newctx, &outbuf, &contextattr, NULL); /* input buffer no longer used */ pfree(buf.data); if (outbuf.cBuffers > 0 && outbuf.pBuffers[0].cbBuffer > 0) { /* * Negotiation generated data to be sent to the client. */ elog(DEBUG4, "sending SSPI response token of length %u", (unsigned int) outbuf.pBuffers[0].cbBuffer); port->gss->outbuf.length = outbuf.pBuffers[0].cbBuffer; port->gss->outbuf.value = outbuf.pBuffers[0].pvBuffer; sendAuthRequest(port, AUTH_REQ_GSS_CONT); FreeContextBuffer(outbuf.pBuffers[0].pvBuffer); } if (r != SEC_E_OK && r != SEC_I_CONTINUE_NEEDED) { if (sspictx != NULL) { DeleteSecurityContext(sspictx); free(sspictx); } FreeCredentialsHandle(&sspicred); pg_SSPI_error(ERROR, gettext_noop("could not accept SSPI security context"), r); } if (sspictx == NULL) { sspictx = malloc(sizeof(CtxtHandle)); if (sspictx == NULL) ereport(ERROR, (errmsg("out of memory"))); memcpy(sspictx, &newctx, sizeof(CtxtHandle)); } if (r == SEC_I_CONTINUE_NEEDED) elog(DEBUG4, "SSPI continue needed"); } while (r == SEC_I_CONTINUE_NEEDED); /* * Release service principal credentials */ FreeCredentialsHandle(&sspicred); /* * SEC_E_OK indicates that authentication is now complete. * * Get the name of the user that authenticated, and compare it to the pg * username that was specified for the connection. * * MingW is missing the export for QuerySecurityContextToken in the * secur32 library, so we have to load it dynamically. */ secur32 = LoadLibrary("SECUR32.DLL"); if (secur32 == NULL) ereport(ERROR, (errmsg_internal("could not load secur32.dll: %d", (int) GetLastError()))); _QuerySecurityContextToken = (QUERY_SECURITY_CONTEXT_TOKEN_FN) GetProcAddress(secur32, "QuerySecurityContextToken"); if (_QuerySecurityContextToken == NULL) { FreeLibrary(secur32); ereport(ERROR, (errmsg_internal("could not locate QuerySecurityContextToken in secur32.dll: %d", (int) GetLastError()))); } r = (_QuerySecurityContextToken) (sspictx, &token); if (r != SEC_E_OK) { FreeLibrary(secur32); pg_SSPI_error(ERROR, gettext_noop("could not get security token from context"), r); } FreeLibrary(secur32); /* * No longer need the security context, everything from here on uses the * token instead. */ DeleteSecurityContext(sspictx); free(sspictx); if (!GetTokenInformation(token, TokenUser, NULL, 0, &retlen) && GetLastError() != 122) ereport(ERROR, (errmsg_internal("could not get token user size: error code %d", (int) GetLastError()))); tokenuser = malloc(retlen); if (tokenuser == NULL) ereport(ERROR, (errmsg("out of memory"))); if (!GetTokenInformation(token, TokenUser, tokenuser, retlen, &retlen)) ereport(ERROR, (errmsg_internal("could not get user token: error code %d", (int) GetLastError()))); if (!LookupAccountSid(NULL, tokenuser->User.Sid, accountname, &accountnamesize, domainname, &domainnamesize, &accountnameuse)) ereport(ERROR, (errmsg_internal("could not lookup acconut sid: error code %d", (int) GetLastError()))); free(tokenuser); /* * Compare realm/domain if requested. In SSPI, always compare case * insensitive. */ if (pg_krb_realm && strlen(pg_krb_realm)) { if (pg_strcasecmp(pg_krb_realm, domainname)) { elog(DEBUG2, "SSPI domain (%s) and configured domain (%s) don't match", domainname, pg_krb_realm); return STATUS_ERROR; } } /* * We have the username (without domain/realm) in accountname, compare to * the supplied value. In SSPI, always compare case insensitive. */ if (pg_strcasecmp(port->user_name, accountname)) { /* GSS name and PGUSER are not equivalent */ elog(DEBUG2, "provided username (%s) and SSPI username (%s) don't match", port->user_name, accountname); return STATUS_ERROR; } return STATUS_OK; }
static int pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq) { int ret; char *crypt_pwd; /* Encrypt the password if needed. */ switch (areq) { case AUTH_REQ_MD5: { char *crypt_pwd2; /* Allocate enough space for two MD5 hashes */ crypt_pwd = malloc(2 * (MD5_PASSWD_LEN + 1)); if (!crypt_pwd) { fprintf(stderr, libpq_gettext("out of memory\n")); return STATUS_ERROR; } crypt_pwd2 = crypt_pwd + MD5_PASSWD_LEN + 1; if (!pg_md5_encrypt(password, conn->pguser, strlen(conn->pguser), crypt_pwd2)) { free(crypt_pwd); return STATUS_ERROR; } if (!pg_md5_encrypt(crypt_pwd2 + strlen("md5"), conn->md5Salt, sizeof(conn->md5Salt), crypt_pwd)) { free(crypt_pwd); return STATUS_ERROR; } break; } case AUTH_REQ_CRYPT: { char salt[3]; StrNCpy(salt, conn->cryptSalt, 3); crypt_pwd = crypt(password, salt); break; } case AUTH_REQ_PASSWORD: /* discard const so we can assign it */ crypt_pwd = (char *) password; break; default: return STATUS_ERROR; } /* Packet has a message type as of protocol 3.0 */ if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) ret = pqPacketSend(conn, 'p', crypt_pwd, strlen(crypt_pwd) + 1); else ret = pqPacketSend(conn, 0, crypt_pwd, strlen(crypt_pwd) + 1); if (areq == AUTH_REQ_MD5) free(crypt_pwd); return ret; }
/* * Receive Startup packet * Response Client Authentication */ int FileRepConnServer_ReceiveStartupPacket(void) { uint32 length; int status = STATUS_OK; char *buf = NULL; pq_init(); status = FileRepConnServer_ReceiveMessageLength(&length); if (status != STATUS_OK) { goto exit; } if (length < (uint32) sizeof(ProtocolVersion) || length > MAX_STARTUP_PACKET_LENGTH) { status = STATUS_ERROR; ereport(WARNING, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("invalid length of startup packet"), FileRep_errcontext())); goto exit; } buf = (char *)malloc(length +1); if (buf == NULL) { ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("not enough memory to allocate buffer for startup packet"), FileRep_errcontext())); } memset(buf, 0, length + 1); if (pq_getbytes(buf, length) == EOF) { status = STATUS_ERROR; ereport(WARNING, (errcode_for_socket_access(), errmsg("receive EOF on connection: %m"), FileRep_errcontext())); goto exit; } port->proto = ntohl(*((ProtocolVersion *) buf)); if (PG_PROTOCOL_MAJOR(port->proto) >= 3) { /* uint32 offset = sizeof(ProtocolVersion);*/ /* * tell the client that it is authorized (no pg_hba.conf and * password are required). */ StringInfoData buf; /* sends AUTH_REQ_OK back to client */ FakeClientAuthentication(port); /* send to client that we are ready to receive data */ /* similar to ReadyForQuery(DestRemoteExecute); */ pq_beginmessage(&buf, 'Z'); pq_sendbyte(&buf, 'I'); pq_endmessage(&buf); pq_flush(); } else { ereport(WARNING, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("not supported version"), FileRep_errcontext())); } exit: if (buf) { free(buf); buf = NULL; } return status; }
static int pg_GSS_recvauth(Port *port) { OM_uint32 maj_stat, min_stat, lmin_s, gflags; int mtype; int ret; StringInfoData buf; gss_buffer_desc gbuf; /* * GSS auth is not supported for protocol versions before 3, because it * relies on the overall message length word to determine the GSS payload * size in AuthenticationGSSContinue and PasswordMessage messages. * (This is, in fact, a design error in our GSS support, because protocol * messages are supposed to be parsable without relying on the length * word; but it's not worth changing it now.) */ if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3) ereport(FATAL, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("GSSAPI is not supported in protocol version 2"))); if (pg_krb_server_keyfile && strlen(pg_krb_server_keyfile) > 0) { /* * Set default Kerberos keytab file for the Krb5 mechanism. * * setenv("KRB5_KTNAME", pg_krb_server_keyfile, 0); except setenv() * not always available. */ if (getenv("KRB5_KTNAME") == NULL) { size_t kt_len = strlen(pg_krb_server_keyfile) + 14; char *kt_path = malloc(kt_len); if (!kt_path) { ereport(LOG, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"))); return STATUS_ERROR; } snprintf(kt_path, kt_len, "KRB5_KTNAME=%s", pg_krb_server_keyfile); putenv(kt_path); } } /* * We accept any service principal that's present in our keytab. This * increases interoperability between kerberos implementations that see * for example case sensitivity differently, while not really opening up * any vector of attack. */ port->gss->cred = GSS_C_NO_CREDENTIAL; /* * Initialize sequence with an empty context */ port->gss->ctx = GSS_C_NO_CONTEXT; /* * Loop through GSSAPI message exchange. This exchange can consist of * multiple messags sent in both directions. First message is always from * the client. All messages from client to server are password packets * (type 'p'). */ do { mtype = pq_getbyte(); if (mtype != 'p') { /* Only log error if client didn't disconnect. */ if (mtype != EOF) ereport(COMMERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("expected GSS response, got message type %d", mtype))); return STATUS_ERROR; } /* Get the actual GSS token */ initStringInfo(&buf); if (pq_getmessage(&buf, 2000)) { /* EOF - pq_getmessage already logged error */ pfree(buf.data); return STATUS_ERROR; } /* Map to GSSAPI style buffer */ gbuf.length = buf.len; gbuf.value = buf.data; elog(DEBUG4, "Processing received GSS token of length %u", (unsigned int) gbuf.length); maj_stat = gss_accept_sec_context( &min_stat, &port->gss->ctx, port->gss->cred, &gbuf, GSS_C_NO_CHANNEL_BINDINGS, &port->gss->name, NULL, &port->gss->outbuf, &gflags, NULL, NULL); /* gbuf no longer used */ pfree(buf.data); elog(DEBUG5, "gss_accept_sec_context major: %d, " "minor: %d, outlen: %u, outflags: %x", maj_stat, min_stat, (unsigned int) port->gss->outbuf.length, gflags); if (port->gss->outbuf.length != 0) { /* * Negotiation generated data to be sent to the client. */ OM_uint32 lmin_s; elog(DEBUG4, "sending GSS response token of length %u", (unsigned int) port->gss->outbuf.length); sendAuthRequest(port, AUTH_REQ_GSS_CONT); gss_release_buffer(&lmin_s, &port->gss->outbuf); } if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) { OM_uint32 lmin_s; gss_delete_sec_context(&lmin_s, &port->gss->ctx, GSS_C_NO_BUFFER); pg_GSS_error(ERROR, gettext_noop("accepting GSS security context failed"), maj_stat, min_stat); } if (maj_stat == GSS_S_CONTINUE_NEEDED) elog(DEBUG4, "GSS continue needed"); } while (maj_stat == GSS_S_CONTINUE_NEEDED); if (port->gss->cred != GSS_C_NO_CREDENTIAL) { /* * Release service principal credentials */ gss_release_cred(&min_stat, &port->gss->cred); } /* * GSS_S_COMPLETE indicates that authentication is now complete. * * Get the name of the user that authenticated, and compare it to the pg * username that was specified for the connection. */ maj_stat = gss_display_name(&min_stat, port->gss->name, &gbuf, NULL); if (maj_stat != GSS_S_COMPLETE) pg_GSS_error(ERROR, gettext_noop("retrieving GSS user name failed"), maj_stat, min_stat); /* * Split the username at the realm separator */ if (strchr(gbuf.value, '@')) { char *cp = strchr(gbuf.value, '@'); *cp = '\0'; cp++; if (pg_krb_realm != NULL && strlen(pg_krb_realm)) { /* * Match the realm part of the name first */ if (pg_krb_caseins_users) ret = pg_strcasecmp(pg_krb_realm, cp); else ret = strcmp(pg_krb_realm, cp); if (ret) { /* GSS realm does not match */ elog(DEBUG2, "GSSAPI realm (%s) and configured realm (%s) don't match", cp, pg_krb_realm); gss_release_buffer(&lmin_s, &gbuf); return STATUS_ERROR; } } } else if (pg_krb_realm && strlen(pg_krb_realm)) { elog(DEBUG2, "GSSAPI did not return realm but realm matching was requested"); gss_release_buffer(&lmin_s, &gbuf); return STATUS_ERROR; } if (pg_krb_caseins_users) ret = pg_strcasecmp(port->user_name, gbuf.value); else ret = strcmp(port->user_name, gbuf.value); if (ret) { /* GSS name and PGUSER are not equivalent */ elog(DEBUG2, "provided username (%s) and GSSAPI username (%s) don't match", port->user_name, (char *) gbuf.value); gss_release_buffer(&lmin_s, &gbuf); return STATUS_ERROR; } gss_release_buffer(&lmin_s, &gbuf); return STATUS_OK; }
static void MPPnoticeReceiver(void * arg, const PGresult * res) { PQExpBufferData msgbuf; PGMessageField *pfield; int elevel = INFO; char * sqlstate = "00000"; char * severity = "WARNING"; char * file = ""; char * line = NULL; char * func = ""; char message[1024]; char * detail = NULL; char * hint = NULL; char * context = NULL; SegmentDatabaseDescriptor *segdbDesc = (SegmentDatabaseDescriptor *) arg; if (!res) return; strcpy(message,"missing error text"); for (pfield = res->errFields; pfield != NULL; pfield = pfield->next) { switch (pfield->code) { case PG_DIAG_SEVERITY: severity = pfield->contents; if (strcmp(pfield->contents,"WARNING")==0) elevel = WARNING; else if (strcmp(pfield->contents,"NOTICE")==0) elevel = NOTICE; else if (strcmp(pfield->contents,"DEBUG1")==0 || strcmp(pfield->contents,"DEBUG")==0) elevel = DEBUG1; else if (strcmp(pfield->contents,"DEBUG2")==0) elevel = DEBUG2; else if (strcmp(pfield->contents,"DEBUG3")==0) elevel = DEBUG3; else if (strcmp(pfield->contents,"DEBUG4")==0) elevel = DEBUG4; else if (strcmp(pfield->contents,"DEBUG5")==0) elevel = DEBUG5; else elevel = INFO; break; case PG_DIAG_SQLSTATE: sqlstate = pfield->contents; break; case PG_DIAG_MESSAGE_PRIMARY: strncpy(message, pfield->contents, 800); message[800] = '\0'; if (segdbDesc && segdbDesc->whoami && strlen(segdbDesc->whoami) < 200) { strcat(message," ("); strcat(message, segdbDesc->whoami); strcat(message,")"); } break; case PG_DIAG_MESSAGE_DETAIL: detail = pfield->contents; break; case PG_DIAG_MESSAGE_HINT: hint = pfield->contents; break; case PG_DIAG_STATEMENT_POSITION: case PG_DIAG_INTERNAL_POSITION: case PG_DIAG_INTERNAL_QUERY: break; case PG_DIAG_CONTEXT: context = pfield->contents; break; case PG_DIAG_SOURCE_FILE: file = pfield->contents; break; case PG_DIAG_SOURCE_LINE: line = pfield->contents; break; case PG_DIAG_SOURCE_FUNCTION: func = pfield->contents; break; case PG_DIAG_GP_PROCESS_TAG: break; default: break; } } if (elevel < client_min_messages && elevel != INFO) return; /* * We use PQExpBufferData instead of StringInfoData * because the former uses malloc, the latter palloc. * We are in a thread, and we CANNOT use palloc since it's not * thread safe. We cannot call elog or ereport either for the * same reason. */ initPQExpBuffer(&msgbuf); if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3) { /* New style with separate fields */ appendPQExpBufferChar(&msgbuf, PG_DIAG_SEVERITY); appendBinaryPQExpBuffer(&msgbuf, severity, strlen(severity)+1); appendPQExpBufferChar(&msgbuf, PG_DIAG_SQLSTATE); appendBinaryPQExpBuffer(&msgbuf, sqlstate, strlen(sqlstate)+1); /* M field is required per protocol, so always send something */ appendPQExpBufferChar(&msgbuf, PG_DIAG_MESSAGE_PRIMARY); appendBinaryPQExpBuffer(&msgbuf, message , strlen(message) + 1); if (detail) { appendPQExpBufferChar(&msgbuf, PG_DIAG_MESSAGE_DETAIL); appendBinaryPQExpBuffer(&msgbuf, detail, strlen(detail)+1); } if (hint) { appendPQExpBufferChar(&msgbuf, PG_DIAG_MESSAGE_HINT); appendBinaryPQExpBuffer(&msgbuf, hint, strlen(hint)+1); } if (context) { appendPQExpBufferChar(&msgbuf, PG_DIAG_CONTEXT); appendBinaryPQExpBuffer(&msgbuf, context, strlen(context)+1); } if (file) { appendPQExpBufferChar(&msgbuf, PG_DIAG_SOURCE_FILE); appendBinaryPQExpBuffer(&msgbuf, file, strlen(file)+1); } if (line) { appendPQExpBufferChar(&msgbuf, PG_DIAG_SOURCE_LINE); appendBinaryPQExpBuffer(&msgbuf, line, strlen(line)+1); } if (func) { appendPQExpBufferChar(&msgbuf, PG_DIAG_SOURCE_FUNCTION); appendBinaryPQExpBuffer(&msgbuf, func, strlen(func)+1); } } else { appendPQExpBuffer(&msgbuf, "%s: ", severity); appendBinaryPQExpBuffer(&msgbuf, message, strlen(message)); appendPQExpBufferChar(&msgbuf, '\n'); appendPQExpBufferChar(&msgbuf, '\0'); } appendPQExpBufferChar(&msgbuf, '\0'); /* terminator */ pq_putmessage('N', msgbuf.data, msgbuf.len); termPQExpBuffer(&msgbuf); pq_flush(); }
/* * SendRowDescriptionMessage --- send a RowDescription message to the frontend * * Notes: the TupleDesc has typically been manufactured by ExecTypeFromTL() * or some similar function; it does not contain a full set of fields. * The targetlist will be NIL when executing a utility function that does * not have a plan. If the targetlist isn't NIL then it is a Query node's * targetlist; it is up to us to ignore resjunk columns in it. The formats[] * array pointer might be NULL (if we are doing Describe on a prepared stmt); * send zeroes for the format codes in that case. */ void SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist, int16 *formats) { Form_pg_attribute *attrs = typeinfo->attrs; int natts = typeinfo->natts; int proto = PG_PROTOCOL_MAJOR(FrontendProtocol); int i; StringInfoData buf; ListCell *tlist_item = list_head(targetlist); pq_beginmessage(&buf, 'T'); /* tuple descriptor message type */ pq_sendint(&buf, natts, 2); /* # of attrs in tuples */ for (i = 0; i < natts; ++i) { Oid atttypid = attrs[i]->atttypid; int32 atttypmod = attrs[i]->atttypmod; pq_sendstring(&buf, NameStr(attrs[i]->attname)); #ifdef PGXC /* * Send the type name from a Postgres-XC backend node. * This preserves from OID inconsistencies as architecture is shared nothing. */ if (IsConnFromCoord()) { char *typenamespace = NULL; char *typename; Oid nspid; nspid = get_typenamespace(atttypid); if (OidIsValid(nspid) && nspid != PG_CATALOG_NAMESPACE) { typenamespace = get_namespace_name(nspid); } typename = get_typename(atttypid); pq_sendstring(&buf, quote_qualified_identifier(typenamespace, typename)); } #endif /* column ID info appears in protocol 3.0 and up */ if (proto >= 3) { /* Do we have a non-resjunk tlist item? */ while (tlist_item && ((TargetEntry *) lfirst(tlist_item))->resjunk) tlist_item = lnext(tlist_item); if (tlist_item) { TargetEntry *tle = (TargetEntry *) lfirst(tlist_item); pq_sendint(&buf, tle->resorigtbl, 4); pq_sendint(&buf, tle->resorigcol, 2); tlist_item = lnext(tlist_item); } else { /* No info available, so send zeroes */ pq_sendint(&buf, 0, 4); pq_sendint(&buf, 0, 2); } } /* If column is a domain, send the base type and typmod instead */ atttypid = getBaseTypeAndTypmod(atttypid, &atttypmod); pq_sendint(&buf, (int) atttypid, sizeof(atttypid)); pq_sendint(&buf, attrs[i]->attlen, sizeof(attrs[i]->attlen)); /* typmod appears in protocol 2.0 and up */ if (proto >= 2) pq_sendint(&buf, atttypmod, sizeof(atttypmod)); /* format info appears in protocol 3.0 and up */ if (proto >= 3) { if (formats) pq_sendint(&buf, formats[i], 2); else pq_sendint(&buf, 0, 2); } } pq_endmessage(&buf); }