static enum STATE read_attr ( FILE* file, struct xml_element* elem ) { enum STATE state; skip_space( file ); int c = fgetc( file ); if ( c == EOF || c == '=' ) return PARSE_ERROR; if ( c == '/' ) { skip_space( file ); c = fgetc( file ); return ( c == '>' ) ? ISOLATED_TAG : PARSE_ERROR; } if ( c == '>' ) return OPEN_TAG; ungetc( c, file ); struct xml_attribute* attr = calloc( 1, sizeof( struct xml_attribute ) ); if ( !attr ) return MEMORY_ERROR; attr->father = elem; attr->status = IS_ATTRIBUTE_STATUS; state = read_attr_name( file, attr ); if ( state != OK ) { free( attr ); return state; } c = fgetc( file ); if ( c != '=' ) { free( attr->name ); free( attr ); return PARSE_ERROR; } skip_space( file ); state = read_attr_value( file, attr ); if ( state != OK ) { free( attr->name ); free( attr ); return state; } state = read_attr( file, elem ); attr->next = elem->attr; elem->attr = attr; if ( attr->next ) attr->next->prev = attr; return state; }
int main(int argc, char *argv[]) { struct termios settings; const int fd = open(serport, O_RDWR); int modules; int i; if (fd < 0) { printf("Failed to open %s due to: %s\n", serport, strerror(errno)); goto failed_open; } if (tcgetattr(fd, &settings) < 0) { perror("Failed to get attributes\n"); goto failed_attr; } printf("before: c_lflag = %x, c_cflag = %x\n", settings.c_lflag, settings.c_cflag); if (cfsetspeed(&settings, B9600) < 0) { perror("Failed to set attributes\n"); goto failed_attr; } settings.c_cflag |= CLOCAL; settings.c_cflag &= ~CSTOPB; settings.c_cflag &= ~CSIZE; settings.c_cflag |= CS8; printf("after: c_lflag = %x, c_cflag = %x\n", settings.c_lflag, settings.c_cflag); if (tcsetattr(fd, TCSANOW, &settings) < 0) { perror("Failed to set attributes\n"); goto failed_attr; } modules = read_no_of_modules(fd); for (i=0; i < modules; i++) { const int attrs = read_no_of_attributes(fd, i); int j; for (j=0; j < attrs; j++) { struct dev_attr_info attr; read_attr_def(fd, i, j, &attr); read_attr_value(fd, i, j, 0xFF, attr.type); } } failed_attr: close(fd); failed_open: return 0; }
/* * Read and parse the final message received from client. */ static void read_client_final_message(scram_state *state, char *input) { char attr; char *channel_binding; char *value; char *begin, *proof; char *p; char *client_proof; begin = p = pstrdup(input); /*------ * The syntax for the server-first-message is: (RFC 5802) * * gs2-header = gs2-cbind-flag "," [ authzid ] "," * ;; GS2 header for SCRAM * ;; (the actual GS2 header includes an optional * ;; flag to indicate that the GSS mechanism is not * ;; "standard", but since SCRAM is "standard", we * ;; don't include that flag). * * cbind-input = gs2-header [ cbind-data ] * ;; cbind-data MUST be present for * ;; gs2-cbind-flag of "p" and MUST be absent * ;; for "y" or "n". * * channel-binding = "c=" base64 * ;; base64 encoding of cbind-input. * * proof = "p=" base64 * * client-final-message-without-proof = * channel-binding "," nonce ["," * extensions] * * client-final-message = * client-final-message-without-proof "," proof *------ */ /* * Read channel-binding. We don't support channel binding, so it's * expected to always be "biws", which is "n,,", base64-encoded, or * "eSws", which is "y,,". We also have to check whether the flag is * the same one that the client originally sent. */ channel_binding = read_attr_value(&p, 'c'); if (!(strcmp(channel_binding, "biws") == 0 && state->cbind_flag == 'n') && !(strcmp(channel_binding, "eSws") == 0 && state->cbind_flag == 'y')) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), (errmsg("unexpected SCRAM channel-binding attribute in client-final-message")))); state->client_final_nonce = read_attr_value(&p, 'r'); /* ignore optional extensions */ do { proof = p - 1; value = read_any_attr(&p, &attr); } while (attr != 'p'); client_proof = palloc(pg_b64_dec_len(strlen(value))); if (pg_b64_decode(value, strlen(value), client_proof) != SCRAM_KEY_LEN) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("malformed SCRAM message"), errdetail("Malformed proof in client-final-message."))); memcpy(state->ClientProof, client_proof, SCRAM_KEY_LEN); pfree(client_proof); if (*p != '\0') ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("malformed SCRAM message"), errdetail("Garbage found at the end of client-final-message."))); state->client_final_message_without_proof = palloc(proof - begin + 1); memcpy(state->client_final_message_without_proof, input, proof - begin); state->client_final_message_without_proof[proof - begin] = '\0'; }
/* * Read and parse the first message from client in the context of a SCRAM * authentication exchange message. * * At this stage, any errors will be reported directly with ereport(ERROR). */ static void read_client_first_message(scram_state *state, char *input) { input = pstrdup(input); /*------ * The syntax for the client-first-message is: (RFC 5802) * * saslname = 1*(value-safe-char / "=2C" / "=3D") * ;; Conforms to <value>. * * authzid = "a=" saslname * ;; Protocol specific. * * cb-name = 1*(ALPHA / DIGIT / "." / "-") * ;; See RFC 5056, Section 7. * ;; E.g., "tls-server-end-point" or * ;; "tls-unique". * * gs2-cbind-flag = ("p=" cb-name) / "n" / "y" * ;; "n" -> client doesn't support channel binding. * ;; "y" -> client does support channel binding * ;; but thinks the server does not. * ;; "p" -> client requires channel binding. * ;; The selected channel binding follows "p=". * * gs2-header = gs2-cbind-flag "," [ authzid ] "," * ;; GS2 header for SCRAM * ;; (the actual GS2 header includes an optional * ;; flag to indicate that the GSS mechanism is not * ;; "standard", but since SCRAM is "standard", we * ;; don't include that flag). * * username = "******" saslname * ;; Usernames are prepared using SASLprep. * * reserved-mext = "m=" 1*(value-char) * ;; Reserved for signaling mandatory extensions. * ;; The exact syntax will be defined in * ;; the future. * * nonce = "r=" c-nonce [s-nonce] * ;; Second part provided by server. * * c-nonce = printable * * client-first-message-bare = * [reserved-mext ","] * username "," nonce ["," extensions] * * client-first-message = * gs2-header client-first-message-bare * * For example: * n,,n=user,r=fyko+d2lbbFgONRv9qkxdawL * * The "n,," in the beginning means that the client doesn't support * channel binding, and no authzid is given. "n=user" is the username. * However, in PostgreSQL the username is sent in the startup packet, and * the username in the SCRAM exchange is ignored. libpq always sends it * as an empty string. The last part, "r=fyko+d2lbbFgONRv9qkxdawL" is * the client nonce. *------ */ /* read gs2-cbind-flag */ state->cbind_flag = *input; switch (*input) { case 'n': /* Client does not support channel binding */ input++; break; case 'y': /* Client supports channel binding, but we're not doing it today */ input++; break; case 'p': /* * Client requires channel binding. We don't support it. * * RFC 5802 specifies a particular error code, * e=server-does-support-channel-binding, for this. But it can * only be sent in the server-final message, and we don't want to * go through the motions of the authentication, knowing it will * fail, just to send that error message. */ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("client requires SCRAM channel binding, but it is not supported"))); default: ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("malformed SCRAM message"), errdetail("Unexpected channel-binding flag \"%s\".", sanitize_char(*input)))); } if (*input != ',') ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("malformed SCRAM message"), errdetail("Comma expected, but found character \"%s\".", sanitize_char(*input)))); input++; /* * Forbid optional authzid (authorization identity). We don't support it. */ if (*input == 'a') ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("client uses authorization identity, but it is not supported"))); if (*input != ',') ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("malformed SCRAM message"), errdetail("Unexpected attribute \"%s\" in client-first-message.", sanitize_char(*input)))); input++; state->client_first_message_bare = pstrdup(input); /* * Any mandatory extensions would go here. We don't support any. * * RFC 5802 specifies error code "e=extensions-not-supported" for this, * but it can only be sent in the server-final message. We prefer to fail * immediately (which the RFC also allows). */ if (*input == 'm') ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("client requires an unsupported SCRAM extension"))); /* * Read username. Note: this is ignored. We use the username from the * startup message instead, still it is kept around if provided as it * proves to be useful for debugging purposes. */ state->client_username = read_attr_value(&input, 'n'); /* read nonce and check that it is made of only printable characters */ state->client_nonce = read_attr_value(&input, 'r'); if (!is_scram_printable(state->client_nonce)) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("non-printable characters in SCRAM nonce"))); /* * There can be any number of optional extensions after this. We don't * support any extensions, so ignore them. */ while (*input != '\0') read_any_attr(&input, NULL); /* success! */ }
/* * Read and parse the first message from client in the context of a SCRAM * authentication exchange message. * * At this stage, any errors will be reported directly with ereport(ERROR). */ static void read_client_first_message(scram_state *state, char *input) { char *channel_binding_type; input = pstrdup(input); /*------ * The syntax for the client-first-message is: (RFC 5802) * * saslname = 1*(value-safe-char / "=2C" / "=3D") * ;; Conforms to <value>. * * authzid = "a=" saslname * ;; Protocol specific. * * cb-name = 1*(ALPHA / DIGIT / "." / "-") * ;; See RFC 5056, Section 7. * ;; E.g., "tls-server-end-point" or * ;; "tls-unique". * * gs2-cbind-flag = ("p=" cb-name) / "n" / "y" * ;; "n" -> client doesn't support channel binding. * ;; "y" -> client does support channel binding * ;; but thinks the server does not. * ;; "p" -> client requires channel binding. * ;; The selected channel binding follows "p=". * * gs2-header = gs2-cbind-flag "," [ authzid ] "," * ;; GS2 header for SCRAM * ;; (the actual GS2 header includes an optional * ;; flag to indicate that the GSS mechanism is not * ;; "standard", but since SCRAM is "standard", we * ;; don't include that flag). * * username = "******" saslname * ;; Usernames are prepared using SASLprep. * * reserved-mext = "m=" 1*(value-char) * ;; Reserved for signaling mandatory extensions. * ;; The exact syntax will be defined in * ;; the future. * * nonce = "r=" c-nonce [s-nonce] * ;; Second part provided by server. * * c-nonce = printable * * client-first-message-bare = * [reserved-mext ","] * username "," nonce ["," extensions] * * client-first-message = * gs2-header client-first-message-bare * * For example: * n,,n=user,r=fyko+d2lbbFgONRv9qkxdawL * * The "n,," in the beginning means that the client doesn't support * channel binding, and no authzid is given. "n=user" is the username. * However, in PostgreSQL the username is sent in the startup packet, and * the username in the SCRAM exchange is ignored. libpq always sends it * as an empty string. The last part, "r=fyko+d2lbbFgONRv9qkxdawL" is * the client nonce. *------ */ /* * Read gs2-cbind-flag. (For details see also RFC 5802 Section 6 "Channel * Binding".) */ state->cbind_flag = *input; switch (*input) { case 'n': /* * The client does not support channel binding or has simply * decided to not use it. In that case just let it go. */ if (state->channel_binding_in_use) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("malformed SCRAM message"), errdetail("The client selected SCRAM-SHA-256-PLUS, but the SCRAM message does not include channel binding data."))); input++; if (*input != ',') ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("malformed SCRAM message"), errdetail("Comma expected, but found character \"%s\".", sanitize_char(*input)))); input++; break; case 'y': /* * The client supports channel binding and thinks that the server * does not. In this case, the server must fail authentication if * it supports channel binding. */ if (state->channel_binding_in_use) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("malformed SCRAM message"), errdetail("The client selected SCRAM-SHA-256-PLUS, but the SCRAM message does not include channel binding data."))); #ifdef HAVE_BE_TLS_GET_CERTIFICATE_HASH if (state->port->ssl_in_use) ereport(ERROR, (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), errmsg("SCRAM channel binding negotiation error"), errdetail("The client supports SCRAM channel binding but thinks the server does not. " "However, this server does support channel binding."))); #endif input++; if (*input != ',') ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("malformed SCRAM message"), errdetail("Comma expected, but found character \"%s\".", sanitize_char(*input)))); input++; break; case 'p': /* * The client requires channel binding. Channel binding type * follows, e.g., "p=tls-server-end-point". */ if (!state->channel_binding_in_use) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("malformed SCRAM message"), errdetail("The client selected SCRAM-SHA-256 without channel binding, but the SCRAM message includes channel binding data."))); channel_binding_type = read_attr_value(&input, 'p'); /* * The only channel binding type we support is * tls-server-end-point. */ if (strcmp(channel_binding_type, "tls-server-end-point") != 0) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), (errmsg("unsupported SCRAM channel-binding type \"%s\"", sanitize_str(channel_binding_type))))); break; default: ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("malformed SCRAM message"), errdetail("Unexpected channel-binding flag \"%s\".", sanitize_char(*input)))); } /* * Forbid optional authzid (authorization identity). We don't support it. */ if (*input == 'a') ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("client uses authorization identity, but it is not supported"))); if (*input != ',') ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("malformed SCRAM message"), errdetail("Unexpected attribute \"%s\" in client-first-message.", sanitize_char(*input)))); input++; state->client_first_message_bare = pstrdup(input); /* * Any mandatory extensions would go here. We don't support any. * * RFC 5802 specifies error code "e=extensions-not-supported" for this, * but it can only be sent in the server-final message. We prefer to fail * immediately (which the RFC also allows). */ if (*input == 'm') ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("client requires an unsupported SCRAM extension"))); /* * Read username. Note: this is ignored. We use the username from the * startup message instead, still it is kept around if provided as it * proves to be useful for debugging purposes. */ state->client_username = read_attr_value(&input, 'n'); /* read nonce and check that it is made of only printable characters */ state->client_nonce = read_attr_value(&input, 'r'); if (!is_scram_printable(state->client_nonce)) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("non-printable characters in SCRAM nonce"))); /* * There can be any number of optional extensions after this. We don't * support any extensions, so ignore them. */ while (*input != '\0') read_any_attr(&input, NULL); /* success! */ }
/* * Read and parse the final message received from client. */ static void read_client_final_message(scram_state *state, char *input) { char attr; char *channel_binding; char *value; char *begin, *proof; char *p; char *client_proof; begin = p = pstrdup(input); /*------ * The syntax for the server-first-message is: (RFC 5802) * * gs2-header = gs2-cbind-flag "," [ authzid ] "," * ;; GS2 header for SCRAM * ;; (the actual GS2 header includes an optional * ;; flag to indicate that the GSS mechanism is not * ;; "standard", but since SCRAM is "standard", we * ;; don't include that flag). * * cbind-input = gs2-header [ cbind-data ] * ;; cbind-data MUST be present for * ;; gs2-cbind-flag of "p" and MUST be absent * ;; for "y" or "n". * * channel-binding = "c=" base64 * ;; base64 encoding of cbind-input. * * proof = "p=" base64 * * client-final-message-without-proof = * channel-binding "," nonce ["," * extensions] * * client-final-message = * client-final-message-without-proof "," proof *------ */ /* * Read channel binding. This repeats the channel-binding flags and is * then followed by the actual binding data depending on the type. */ channel_binding = read_attr_value(&p, 'c'); if (state->channel_binding_in_use) { #ifdef HAVE_BE_TLS_GET_CERTIFICATE_HASH const char *cbind_data = NULL; size_t cbind_data_len = 0; size_t cbind_header_len; char *cbind_input; size_t cbind_input_len; char *b64_message; int b64_message_len; Assert(state->cbind_flag == 'p'); /* Fetch hash data of server's SSL certificate */ cbind_data = be_tls_get_certificate_hash(state->port, &cbind_data_len); /* should not happen */ if (cbind_data == NULL || cbind_data_len == 0) elog(ERROR, "could not get server certificate hash"); cbind_header_len = strlen("p=tls-server-end-point,,"); /* p=type,, */ cbind_input_len = cbind_header_len + cbind_data_len; cbind_input = palloc(cbind_input_len); snprintf(cbind_input, cbind_input_len, "p=tls-server-end-point,,"); memcpy(cbind_input + cbind_header_len, cbind_data, cbind_data_len); b64_message = palloc(pg_b64_enc_len(cbind_input_len) + 1); b64_message_len = pg_b64_encode(cbind_input, cbind_input_len, b64_message); b64_message[b64_message_len] = '\0'; /* * Compare the value sent by the client with the value expected by the * server. */ if (strcmp(channel_binding, b64_message) != 0) ereport(ERROR, (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), (errmsg("SCRAM channel binding check failed")))); #else /* shouldn't happen, because we checked this earlier already */ elog(ERROR, "channel binding not supported by this build"); #endif } else { /* * If we are not using channel binding, the binding data is expected * to always be "biws", which is "n,," base64-encoded, or "eSws", * which is "y,,". We also have to check whether the flag is the same * one that the client originally sent. */ if (!(strcmp(channel_binding, "biws") == 0 && state->cbind_flag == 'n') && !(strcmp(channel_binding, "eSws") == 0 && state->cbind_flag == 'y')) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), (errmsg("unexpected SCRAM channel-binding attribute in client-final-message")))); } state->client_final_nonce = read_attr_value(&p, 'r'); /* ignore optional extensions */ do { proof = p - 1; value = read_any_attr(&p, &attr); } while (attr != 'p'); client_proof = palloc(pg_b64_dec_len(strlen(value))); if (pg_b64_decode(value, strlen(value), client_proof) != SCRAM_KEY_LEN) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("malformed SCRAM message"), errdetail("Malformed proof in client-final-message."))); memcpy(state->ClientProof, client_proof, SCRAM_KEY_LEN); pfree(client_proof); if (*p != '\0') ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("malformed SCRAM message"), errdetail("Garbage found at the end of client-final-message."))); state->client_final_message_without_proof = palloc(proof - begin + 1); memcpy(state->client_final_message_without_proof, input, proof - begin); state->client_final_message_without_proof[proof - begin] = '\0'; }