/* calculate hash for authentication using packet contents */ void wd_calc_hash(const char *str, int len, char *buf) { char pass[(MAX_PASSWORD_SIZE + 1) / 2]; char username[(MAX_PASSWORD_SIZE + 1) / 2]; size_t pass_len; size_t username_len; size_t authkey_len; /* use first half of authkey as username, last half as password */ authkey_len = strlen(pool_config->wd_authkey); username_len = authkey_len / 2; pass_len = authkey_len - username_len; snprintf(username, username_len + 1, "%s", pool_config->wd_authkey); snprintf(pass, pass_len + 1, "%s", pool_config->wd_authkey + username_len); /* calculate hash using md5 encrypt */ pool_md5_encrypt(pass, username, strlen(username), buf + MD5_PASSWD_LEN + 1); buf[(MD5_PASSWD_LEN+1)*2-1] = '\0'; pool_md5_encrypt(buf+MD5_PASSWD_LEN+1, str, len, buf); buf[MD5_PASSWD_LEN] = '\0'; }
/* -------------------------------- * pcp_authorize - authenticate with pgpool using username and password * * return 0 on success, -1 otherwise * -------------------------------- */ static int pcp_authorize(char *username, char *password) { char tos; char *buf = NULL; int wsize; int rsize; char salt[4]; char encrypt_buf[(MD5_PASSWD_LEN+1)*2]; char md5[MD5_PASSWD_LEN+1]; /* request salt */ pcp_write(pc, "M", 1); wsize = htonl(sizeof(int)); pcp_write(pc, &wsize, sizeof(int)); if (pcp_flush(pc) < 0) { if (debug) fprintf(stderr, "DEBUG: could not send data to backend\n"); return -1; } if (pcp_read(pc, &tos, 1)) return -1; if (pcp_read(pc, &rsize, sizeof(int))) return -1; rsize = ntohl(rsize); buf = (char *)malloc(rsize); if (buf == NULL) { errorcode = NOMEMERR; return -1; } if (pcp_read(pc, buf, rsize - sizeof(int))) return -1; memcpy(salt, buf, 4); free(buf); /* encrypt password */ pool_md5_hash(password, strlen(password), md5); md5[MD5_PASSWD_LEN] = '\0'; pool_md5_encrypt(md5, username, strlen(username), encrypt_buf + MD5_PASSWD_LEN + 1); encrypt_buf[(MD5_PASSWD_LEN+1)*2-1] = '\0'; pool_md5_encrypt(encrypt_buf+MD5_PASSWD_LEN+1, salt, 4, encrypt_buf); encrypt_buf[MD5_PASSWD_LEN] = '\0'; pcp_write(pc, "R", 1); wsize = htonl((strlen(username)+1 + strlen(encrypt_buf)+1) + sizeof(int)); pcp_write(pc, &wsize, sizeof(int)); pcp_write(pc, username, strlen(username)+1); pcp_write(pc, encrypt_buf, strlen(encrypt_buf)+1); if (pcp_flush(pc) < 0) { if (debug) fprintf(stderr, "DEBUG: could not send data to backend\n"); return -1; } if (debug) fprintf(stderr, "DEBUG: send: tos=\"R\", len=%d\n", ntohl(wsize)); if (pcp_read(pc, &tos, 1)) return -1; if (pcp_read(pc, &rsize, sizeof(int))) return -1; rsize = ntohl(rsize); buf = (char *)malloc(rsize); if (buf == NULL) { errorcode = NOMEMERR; return -1; } if (pcp_read(pc, buf, rsize - sizeof(int))) return -1; if (debug) fprintf(stderr, "DEBUG: recv: tos=\"%c\", len=%d, data=%s\n", tos, rsize, buf); if (tos == 'e') { if (debug) fprintf(stderr, "DEBUG: command failed. reason=%s\n", buf); errorcode = BACKENDERR; } else if (tos == 'r') { if (strcmp(buf, "AuthenticationOK") == 0) { free(buf); return 0; } if (debug) fprintf(stderr, "DEBUG: authentication failed. reason=%s\n", buf); errorcode = AUTHERR; } free(buf); return -1; }
/* * see if received username and password matches with one in the file */ static int user_authenticate(char *buf, char *passwd_file, char *salt, int salt_len) { FILE *fp = NULL; char packet_username[MAX_USER_PASSWD_LEN+1]; char packet_password[MAX_USER_PASSWD_LEN+1]; char encrypt_buf[(MD5_PASSWD_LEN+1)*2]; char file_username[MAX_USER_PASSWD_LEN+1]; char file_password[MAX_USER_PASSWD_LEN+1]; char *index = NULL; static char line[MAX_FILE_LINE_LEN+1]; int i, len; /* strcpy() should be OK, but use strncpy() to be extra careful */ strncpy(packet_username, buf, MAX_USER_PASSWD_LEN); index = (char *) memchr(buf, '\0', MAX_USER_PASSWD_LEN); if (index == NULL) { ereport(FATAL, (errmsg("failed to authenticate PCP user"), errdetail("error while reading authentication packet"))); return 0; } strncpy(packet_password, ++index, MAX_USER_PASSWD_LEN); fp = fopen(passwd_file, "r"); if (fp == NULL) { ereport(FATAL, (errmsg("failed to authenticate PCP user"), errdetail("could not open %s. reason: %s", passwd_file, strerror(errno)))); return 0; } /* for now, I don't care if duplicate username exists in the config file */ while ((fgets(line, MAX_FILE_LINE_LEN, fp)) != NULL) { i = 0; len = 0; if (line[0] == '\n' || line[0] == '#') continue; while (line[i] != ':') { len++; if (++i > MAX_USER_PASSWD_LEN) { fclose(fp); ereport(FATAL, (errmsg("failed to authenticate PCP user"), errdetail("username read from file \"%s\" is larger than maximum allowed username length [%d]", passwd_file, MAX_USER_PASSWD_LEN))); return 0; } } memcpy(file_username, line, len); file_username[len] = '\0'; if (strcmp(packet_username, file_username) != 0) continue; i++; len = 0; while (line[i] != '\n' && line[i] != '\0') { len++; if (++i > MAX_USER_PASSWD_LEN) { fclose(fp); ereport(FATAL, (errmsg("failed to authenticate PCP user"), errdetail("password read from file \"%s\" is larger than maximum allowed password length [%d]", passwd_file, MAX_USER_PASSWD_LEN))); return 0; } } memcpy(file_password, line+strlen(file_username)+1, len); file_password[len] = '\0'; pool_md5_encrypt(file_password, file_username, strlen(file_username), encrypt_buf + MD5_PASSWD_LEN + 1); encrypt_buf[(MD5_PASSWD_LEN+1)*2-1] = '\0'; pool_md5_encrypt(encrypt_buf+MD5_PASSWD_LEN+1, salt, salt_len, encrypt_buf); encrypt_buf[MD5_PASSWD_LEN] = '\0'; if (strcmp(encrypt_buf, packet_password) == 0) { fclose(fp); return 1; } } fclose(fp); ereport(FATAL, (errmsg("authentication failed for user \"%s\"",packet_username), errdetail("username and/or password does not match"))); return 0; }
/* * Do authentication. Assuming the only caller is * *make_persistent_db_connection(). */ static int s_do_auth(POOL_CONNECTION_POOL_SLOT *cp, char *password) { char kind; int status; int length; int auth_kind; char state; char *p; int pid, key; bool keydata_done; /* * read kind expecting 'R' packet (authentication response) */ status = pool_read(cp->con, &kind, sizeof(kind)); if (status < 0) { pool_error("s_do_auth: error while reading message kind"); return -1; } if (kind != 'R') { pool_error("s_do_auth: expecting R got %c", kind); return -1; } /* read message length */ status = pool_read(cp->con, &length, sizeof(length)); if (status < 0) { pool_error("s_do_auth: error while reading message length"); return -1; } length = ntohl(length); /* read auth kind */ status = pool_read(cp->con, &auth_kind, sizeof(auth_kind)); if (status < 0) { pool_error("s_do_auth: error while reading auth kind"); return -1; } auth_kind = ntohl(auth_kind); pool_debug("s_do_auth: auth kind: %d", auth_kind); if (auth_kind == 0) /* trust authentication? */ { cp->con->auth_kind = 0; } else if (auth_kind == 3) /* clear text password? */ { int size = htonl(strlen(password) + 5); pool_write(cp->con, "p", 1); pool_write(cp->con, &size, sizeof(size)); pool_write_and_flush(cp->con, password, strlen(password) + 1); status = pool_flush(cp->con); if (status > 0) { pool_error("s_do_auth: error while sending clear text password"); return -1; } return s_do_auth(cp, password); } else if (auth_kind == 4) /* crypt password? */ { int size; char salt[3]; char *crypt_password; status = pool_read(cp->con, &salt, 2); if (status > 0) { pool_error("s_do_auth: error while reading crypt salt"); return -1; } salt[2] = '\0'; crypt_password = crypt(password, salt); size = htonl(strlen(crypt_password) + 5); pool_write(cp->con, "p", 1); pool_write(cp->con, &size, sizeof(size)); pool_write_and_flush(cp->con, crypt_password, strlen(crypt_password) + 1); status = pool_flush(cp->con); if (status > 0) { pool_error("s_do_auth: error while sending crypt password"); return -1; } return s_do_auth(cp, password); } else if (auth_kind == 5) /* md5 password? */ { char salt[4]; char *buf, *buf1; int size; status = pool_read(cp->con, &salt, 4); if (status > 0) { pool_error("s_do_auth: error while reading md5 salt"); return -1; } buf = malloc(2 * (MD5_PASSWD_LEN + 4)); /* hash + "md5" + '\0' */ if (buf == NULL) { pool_error("s_do_auth(): malloc failed: %s", strerror(errno)); return -1; } memset(buf, 0, 2 * (MD5_PASSWD_LEN + 4)); /* build md5 password */ buf1 = buf + MD5_PASSWD_LEN + 4; pool_md5_encrypt(password, cp->sp->user, strlen(cp->sp->user), buf1); pool_md5_encrypt(buf1, salt, 4, buf + 3); memcpy(buf, "md5", 3); size = htonl(strlen(buf) + 5); pool_write(cp->con, "p", 1); pool_write(cp->con, &size, sizeof(size)); pool_write_and_flush(cp->con, buf, strlen(buf) + 1); status = pool_flush(cp->con); if (status > 0) { pool_error("s_do_auth: error while sending md5 password"); return -1; } status = s_do_auth(cp, password); free(buf); return status; } else { pool_error("s_do_auth: auth kind %d not supported yet", auth_kind); return -1; } /* * Read backend key data and wait until Ready for query arriving or * error happens. */ keydata_done = false; for (;;) { status = pool_read(cp->con, &kind, sizeof(kind)); if (status < 0) { pool_error("s_do_auth: error while reading message kind"); return -1; } switch (kind) { case 'K': /* backend key data */ keydata_done = true; pool_debug("s_do_auth: backend key data received"); /* read message length */ status = pool_read(cp->con, &length, sizeof(length)); if (status < 0) { pool_error("s_do_auth: error while reading message length"); return -1; } if (ntohl(length) != 12) { pool_error("s_do_auth: backend key data length is not 12 (%d)", ntohl(length)); } /* read pid */ if (pool_read(cp->con, &pid, sizeof(pid)) < 0) { pool_error("s_do_auth: failed to read pid"); return -1; } cp->pid = pid; /* read key */ if (pool_read(cp->con, &key, sizeof(key)) < 0) { pool_error("s_do_auth: failed to read key"); return -1; } cp->key = key; break; case 'Z': /* Ready for query */ /* read message length */ status = pool_read(cp->con, &length, sizeof(length)); if (status < 0) { pool_error("s_do_auth: error while reading message length"); return -1; } length = ntohl(length); /* read transaction state */ status = pool_read(cp->con, &state, sizeof(state)); if (status < 0) { pool_error("s_do_auth: error while reading transaction state"); return -1; } pool_debug("s_do_auth: transaction state: %c", state); cp->con->tstate = state; if (!keydata_done) { pool_error("s_do_auth: ready for query arrived before receiving keydata"); } return 0; break; case 'S': /* parameter status */ case 'N': /* notice response */ case 'E': /* error response */ /* Just throw away data */ status = pool_read(cp->con, &length, sizeof(length)); if (status < 0) { pool_error("s_do_auth: error while reading message length. kind:%c", kind); return -1; } length = ntohl(length); length -= 4; p = pool_read2(cp->con, length); if (p == NULL) return -1; break; default: pool_error("s_do_auth: unknown response \"%c\" while processing BackendKeyData", kind); break; } } return -1; }
/* -------------------------------- * pcp_authorize - authenticate with pgpool using username and password * * return 0 on success, -1 otherwise * -------------------------------- */ static int pcp_authorize(PCPConnInfo* pcpConn, char *username, char *password) { int wsize; char salt[4]; char* salt_ptr; char encrypt_buf[(MD5_PASSWD_LEN+1)*2]; char md5[MD5_PASSWD_LEN+1]; PCPResultInfo* pcpRes; if (password == NULL) password = ""; if (username == NULL) username = ""; if (PCPConnectionStatus(pcpConn) != PCP_CONNECTION_CONNECTED) { pcp_internal_error(pcpConn, "ERROR: PCP authorization failed. invalid connection state."); return -1; } if (strlen(username) >= MAX_USER_PASSWD_LEN) { pcp_internal_error(pcpConn, "ERROR: PCP authorization failed. username too long."); return -1; } /* request salt */ pcp_write(pcpConn->pcpConn, "M", 1); wsize = htonl(sizeof(int)); pcp_write(pcpConn->pcpConn, &wsize, sizeof(int)); if (PCPFlush(pcpConn) < 0) return -1; pcpRes = process_pcp_response(pcpConn, 'M'); if(PCPResultStatus(pcpRes) != PCP_RES_COMMAND_OK) return -1; salt_ptr = pcp_get_binary_data(pcpRes, 0); if(salt_ptr == NULL) return -1; memcpy(salt, salt_ptr, 4); /* encrypt password */ pool_md5_hash(password, strlen(password), md5); md5[MD5_PASSWD_LEN] = '\0'; pool_md5_encrypt(md5, username, strlen(username), encrypt_buf + MD5_PASSWD_LEN + 1); encrypt_buf[(MD5_PASSWD_LEN+1)*2-1] = '\0'; pool_md5_encrypt(encrypt_buf+MD5_PASSWD_LEN+1, salt, 4, encrypt_buf); encrypt_buf[MD5_PASSWD_LEN] = '\0'; pcp_write(pcpConn->pcpConn, "R", 1); wsize = htonl((strlen(username)+1 + strlen(encrypt_buf)+1) + sizeof(int)); pcp_write(pcpConn->pcpConn, &wsize, sizeof(int)); pcp_write(pcpConn->pcpConn, username, strlen(username)+1); pcp_write(pcpConn->pcpConn, encrypt_buf, strlen(encrypt_buf)+1); if (PCPFlush(pcpConn) < 0) return -1; pcpRes = process_pcp_response(pcpConn, 'R'); if(PCPResultStatus(pcpRes) != PCP_RES_COMMAND_OK) return -1; pcp_free_result(pcpConn); return 0; }