It's complicated
The wallet.dat file itself is a set of key/value pairs in Berkeley DB BTREE v9 format. Most of it is not encrypted, except for certain values including the private keys themselves and the master key. More specifically:
A 32-byte random master key or "mkey" is generated using OpenSSL's RAND_bytes(). This PRNG is seeded by /dev/urandom on Linux or by CryptGenRandom() on Windows. Additionally on Windows, Bitcoin adds additional entropy from a screen shot and from Windows perfmon counters (possibly because OpenSSL didn't use CryptGenRandom() in early versions?).
The mkey is used as an encryption key to encrypt each individual Bitcoin private key using AES-256 in CBC mode with PKCS7 padding and an initialization vector of SHA-256(SHA-256(the respective public key)).
Your password, plus an 8-byte salt which is initially generated using RAND_bytes() and stored in wallet.dat, is fed into PBKDF1-SHA-512 (normal PBKDF1 doesn't use SHA-512) with a certain number of iterations (stored in wallet.dat) to generate 512 bits of derived key data. The iteration count is initially set such that it will take about 1/10th of a second to run the iterations on whichever CPU the password is added (or modified).
This derived data is divided into three parts. The first 256 bits are used as an encryption key, the next 128 bits are used as in initialization vector, and the remaining bits are discarded. This encryption key and initialization vector are then used to encrypt the mkey (again using using AES-256 in CBC mode with PKCS7 padding), and the encrypted mkey is stored in wallet.dat.
Upon a password change, only the encrypted mkey needs to be recomputed and written back to wallet.dat, while it's unencrypted value and all of the Bitcoin private keys which it encrypts remain unchanged.