diff --git a/packages/auth/providers/credentials/authorization/ldap-authorization.ts b/packages/auth/providers/credentials/authorization/ldap-authorization.ts index 9f441aa9a..22dd15181 100644 --- a/packages/auth/providers/credentials/authorization/ldap-authorization.ts +++ b/packages/auth/providers/credentials/authorization/ldap-authorization.ts @@ -6,14 +6,14 @@ import type { Database, InferInsertModel } from "@homarr/db"; import { and, eq } from "@homarr/db"; import { users } from "@homarr/db/schema"; import { logger } from "@homarr/log"; -import type { userSignInSchema } from "@homarr/validation/user"; +import type { ldapSignInSchema } from "@homarr/validation/user"; import { env } from "../../../env"; import { LdapClient } from "../ldap-client"; export const authorizeWithLdapCredentialsAsync = async ( db: Database, - credentials: z.infer, + credentials: z.infer, ) => { logger.info(`user ${credentials.name} is trying to log in using LDAP. Connecting to LDAP server...`); const client = new LdapClient(); @@ -38,7 +38,14 @@ export const authorizeWithLdapCredentialsAsync = async ( attributes: [env.AUTH_LDAP_USERNAME_ATTRIBUTE, env.AUTH_LDAP_USER_MAIL_ATTRIBUTE], }, }) - .then((entries) => entries.at(0)); + .then((entries) => { + if (entries.length > 1) { + logger.warn(`Multiple LDAP users found for ${credentials.name}, expected only one.`); + throw new CredentialsSignin(); + } + + return entries.at(0); + }); if (!ldapUser) { logger.warn(`User ${credentials.name} not found in LDAP`); diff --git a/packages/auth/providers/credentials/credentials-provider.ts b/packages/auth/providers/credentials/credentials-provider.ts index 79e5eddf6..9de08da14 100644 --- a/packages/auth/providers/credentials/credentials-provider.ts +++ b/packages/auth/providers/credentials/credentials-provider.ts @@ -1,7 +1,7 @@ import type Credentials from "@auth/core/providers/credentials"; import type { Database } from "@homarr/db"; -import { userSignInSchema } from "@homarr/validation/user"; +import { ldapSignInSchema, userSignInSchema } from "@homarr/validation/user"; import { authorizeWithBasicCredentialsAsync } from "./authorization/basic-authorization"; import { authorizeWithLdapCredentialsAsync } from "./authorization/ldap-authorization"; @@ -28,7 +28,7 @@ export const createLdapConfiguration = (db: Database) => name: "Ldap", // eslint-disable-next-line no-restricted-syntax async authorize(credentials) { - const data = await userSignInSchema.parseAsync(credentials); + const data = await ldapSignInSchema.parseAsync(credentials); return await authorizeWithLdapCredentialsAsync(db, data).catch(() => null); }, }) satisfies CredentialsConfiguration; diff --git a/packages/validation/src/user.ts b/packages/validation/src/user.ts index 788eb2750..c00d9226d 100644 --- a/packages/validation/src/user.ts +++ b/packages/validation/src/user.ts @@ -69,6 +69,17 @@ export const userSignInSchema = z.object({ password: z.string().min(1), }); +export const ldapSignInSchema = z.object({ + name: z + .string() + .min(1) + // Prevent special characters that could lead to LDAP injection attacks + .regex(/^[^\\,+<>;"=)(*|!&]+$/, { + message: "Invalid characters in ldap username", + }), + password: z.string().min(1), +}); + export const userRegistrationSchema = addConfirmPasswordRefinement( z.object({ username: usernameSchema,