You were still describing why the change is safe, instead of focusing on
what could go wrong. I updated the section.

** Description changed:

  [ Impact ]
  
   * Local OAuth2 authentication fails if the JWT aud claim is an array.
  From RFC7519:
  
   > In the general case, the "aud" value is an array of case-sensitive strings,
   > each containing a StringOrURI value. In the special case when the JWT has
   > one audience, the "aud" value MAY be a single case-sensitive string
   > containing a StringOrURI value.
  
   * Many IdPs (Keycloak, for example) can be configured to send "aud" as an
     array if the token is meant for multiple audiences.
  
   * The inability to parse array claims breaks compatibility with compliant
     IdPs.
  
  [ Test Plan ]
  
   * On a fresh Ubuntu Noble LXC container, run the following script as
  root.
  
  echo initial setup
  
  apt update && apt install -y python3-jwt python3-cryptography openssl
  dovecot-core
  
  echo key generation and dictionary setup
  
  mkdir -p /etc/dovecot/keys/default/RS256/
  openssl genrsa -out /etc/dovecot/keys/test.pem 2048
  openssl rsa -in /etc/dovecot/keys/test.pem -pubout -out 
/etc/dovecot/keys/default/RS256/default
  find /etc/dovecot/keys -type d -exec chmod 755 {} \;
  chmod 644 /etc/dovecot/keys/default/RS256/default
  
  echo writing minimal dovecot configuration:
  
  cat << EOF > /etc/dovecot/dovecot.conf
  auth_debug = yes
  auth_debug_passwords = yes
  auth_mechanisms = xoauth2 oauthbearer plain
  
  passdb {
      driver = oauth2
      mechanisms = xoauth2 oauthbearer plain
      args = /etc/dovecot/dovecot-oauth2.conf.ext
  }
  
  userdb {
      driver = static
      args = uid=1000 gid=1000 home=/tmp/%u
  }
  EOF
  
  cat << EOF > /etc/dovecot/dovecot-oauth2.conf.ext
  introspection_mode = local
  client_id = dovecot
  local_validation_key_dict = fs:posix:prefix=/etc/dovecot/keys/
  username_attribute = sub
  EOF
  
  systemctl restart dovecot
  
  echo generating test JWT
  
  cat << EOF > gen-jwt.py
  import jwt, time, os
  
  aud = 'dovecot' if 'SINGLE' in os.environ else ['dovecot',
  'https://example.com']
  
  with open('/etc/dovecot/keys/test.pem', 'rb') as f:
      key = f.read()
  
  payload = {
      'iss': 'https://example.com',
      'sub': '[email protected]',
      'aud': aud,
      'exp': int(time.time()) + 3600,
  }
  
  print(jwt.encode(payload, key, algorithm='RS256'))
  EOF
  
  echo trying to authenticate (single aud)
  
  doveadm auth test [email protected] "$(SINGLE=1 python3 gen-jwt.py)"
  
  echo trying to authenticate (multiple aud)
  
  doveadm auth test [email protected] "$(python3 gen-jwt.py)"
  
  [ Where problems could occur ]
  
-  * This is an upstream change and was already fixed in the release branch for
-    2.3.21.
+ Problems with this change would manifest themselves in the oauth2 code,
+ which is the area touched by this fix, particularly with jwt tokens.
+ These could range from regressions (single aud tokens that were working
+ before now break), to new bugs in handling multiple auds. The test plan
+ covers both scenarios to try to mitigate this.
  
-  * The code also handles the scenario where "aud" is a single string, so
-    clients using JWTs with a single "aud" don't break. The test plan above
-    demonstrates this.
- 
-  * Unlikely that this change breaks oauth authentication: the same patch is
-    present in the 2.4.X release series and I couldn't find any bugs reported.
  
  [ Other Info ]
  
   * Fixed upstream in 2.4.0 and local JWT validation was introduced in 2.3.11,
     so this could only possibly affect Jammy and Noble.
  
   * However, the "aud" field validation was only added in 63e0c9e, which is 
only
     present in 2.3.21.1.
  
   * When running the test plan above on a Jammy system, authentication
  works.
  
  [ Original Bug Description ]
  
  Description: Ubuntu 24.04.1 LTS
  Release: 24.04
  dovecot-core/noble-updates 1:2.3.21+dfsg1-2ubuntu6.1
  
  On Ubuntu’s Dovecot build, local OAuth2/JWT validation fails if the JWT
  aud claim is a JSON array. Dovecot logs:
  
  Local validation failed: client_id set but aud is missing
  
  This happens even though aud is present (as an array):
  { "aud": ["dovecot", "https://checkin.thga.de";], ... }
  
  Upstream Dovecot release-2.3.21 uses an array-aware accessor:
  
      get_field_multiple(tree, "aud")
      Source: src/lib-oauth2/oauth2-jwt.c (release-2.3.21 branch)
  
  But Ubuntu appears to be built from code corresponding to the 2.3.21 tag
  where it uses:
  
      get_field(tree, "aud")
      Source: src/lib-oauth2/oauth2-jwt.c (2.3.21 tag)
  
  With get_field(), aud arrays are not handled, so aud is treated as
  missing.

** Description changed:

  [ Impact ]
  
   * Local OAuth2 authentication fails if the JWT aud claim is an array.
  From RFC7519:
  
   > In the general case, the "aud" value is an array of case-sensitive strings,
   > each containing a StringOrURI value. In the special case when the JWT has
   > one audience, the "aud" value MAY be a single case-sensitive string
   > containing a StringOrURI value.
  
   * Many IdPs (Keycloak, for example) can be configured to send "aud" as an
     array if the token is meant for multiple audiences.
  
   * The inability to parse array claims breaks compatibility with compliant
     IdPs.
  
  [ Test Plan ]
  
   * On a fresh Ubuntu Noble LXC container, run the following script as
  root.
  
  echo initial setup
  
  apt update && apt install -y python3-jwt python3-cryptography openssl
  dovecot-core
  
  echo key generation and dictionary setup
  
  mkdir -p /etc/dovecot/keys/default/RS256/
  openssl genrsa -out /etc/dovecot/keys/test.pem 2048
  openssl rsa -in /etc/dovecot/keys/test.pem -pubout -out 
/etc/dovecot/keys/default/RS256/default
  find /etc/dovecot/keys -type d -exec chmod 755 {} \;
  chmod 644 /etc/dovecot/keys/default/RS256/default
  
  echo writing minimal dovecot configuration:
  
  cat << EOF > /etc/dovecot/dovecot.conf
  auth_debug = yes
  auth_debug_passwords = yes
  auth_mechanisms = xoauth2 oauthbearer plain
  
  passdb {
      driver = oauth2
      mechanisms = xoauth2 oauthbearer plain
      args = /etc/dovecot/dovecot-oauth2.conf.ext
  }
  
  userdb {
      driver = static
      args = uid=1000 gid=1000 home=/tmp/%u
  }
  EOF
  
  cat << EOF > /etc/dovecot/dovecot-oauth2.conf.ext
  introspection_mode = local
  client_id = dovecot
  local_validation_key_dict = fs:posix:prefix=/etc/dovecot/keys/
  username_attribute = sub
  EOF
  
  systemctl restart dovecot
  
  echo generating test JWT
  
  cat << EOF > gen-jwt.py
  import jwt, time, os
  
  aud = 'dovecot' if 'SINGLE' in os.environ else ['dovecot',
  'https://example.com']
  
  with open('/etc/dovecot/keys/test.pem', 'rb') as f:
      key = f.read()
  
  payload = {
      'iss': 'https://example.com',
      'sub': '[email protected]',
      'aud': aud,
      'exp': int(time.time()) + 3600,
  }
  
  print(jwt.encode(payload, key, algorithm='RS256'))
  EOF
  
  echo trying to authenticate (single aud)
  
  doveadm auth test [email protected] "$(SINGLE=1 python3 gen-jwt.py)"
  
  echo trying to authenticate (multiple aud)
  
  doveadm auth test [email protected] "$(python3 gen-jwt.py)"
  
  [ Where problems could occur ]
  
  Problems with this change would manifest themselves in the oauth2 code,
  which is the area touched by this fix, particularly with jwt tokens.
  These could range from regressions (single aud tokens that were working
  before now break), to new bugs in handling multiple auds. The test plan
- covers both scenarios to try to mitigate this.
- 
+ covers both scenarios to try to mitigate this. Other authentication
+ mechanisms should remain unchanged.
  
  [ Other Info ]
  
   * Fixed upstream in 2.4.0 and local JWT validation was introduced in 2.3.11,
     so this could only possibly affect Jammy and Noble.
  
   * However, the "aud" field validation was only added in 63e0c9e, which is 
only
     present in 2.3.21.1.
  
   * When running the test plan above on a Jammy system, authentication
  works.
  
  [ Original Bug Description ]
  
  Description: Ubuntu 24.04.1 LTS
  Release: 24.04
  dovecot-core/noble-updates 1:2.3.21+dfsg1-2ubuntu6.1
  
  On Ubuntu’s Dovecot build, local OAuth2/JWT validation fails if the JWT
  aud claim is a JSON array. Dovecot logs:
  
  Local validation failed: client_id set but aud is missing
  
  This happens even though aud is present (as an array):
  { "aud": ["dovecot", "https://checkin.thga.de";], ... }
  
  Upstream Dovecot release-2.3.21 uses an array-aware accessor:
  
      get_field_multiple(tree, "aud")
      Source: src/lib-oauth2/oauth2-jwt.c (release-2.3.21 branch)
  
  But Ubuntu appears to be built from code corresponding to the 2.3.21 tag
  where it uses:
  
      get_field(tree, "aud")
      Source: src/lib-oauth2/oauth2-jwt.c (2.3.21 tag)
  
  With get_field(), aud arrays are not handled, so aud is treated as
  missing.

-- 
You received this bug notification because you are a member of Ubuntu
Bugs, which is subscribed to Ubuntu.
https://bugs.launchpad.net/bugs/2142200

Title:
  dovecot-core: OAuth2 JWT validation fails with client_id set but aud
  is missing when aud claim is an array

To manage notifications about this bug go to:
https://bugs.launchpad.net/ubuntu/+source/dovecot/+bug/2142200/+subscriptions


-- 
ubuntu-bugs mailing list
[email protected]
https://lists.ubuntu.com/mailman/listinfo/ubuntu-bugs

Reply via email to