/// <summary> /// [SAML2.0std] section 2.7.3.1 /// </summary> public void ValidateAttribute(SamlAttribute samlAttribute) { if (samlAttribute == null) { throw new ArgumentNullException("samlAttribute"); } if (!Saml20Utils.ValidateRequiredString(samlAttribute.Name)) { throw new Saml20FormatException("Name attribute of Attribute element MUST contain at least one non-whitespace character"); } if (samlAttribute.AttributeValue != null) { foreach (object o in samlAttribute.AttributeValue) { if (o == null) { throw new Saml20FormatException("null-AttributeValue elements are not supported"); } } } if (samlAttribute.AnyAttr != null) { AnyAttrValidator.ValidateXmlAnyAttributes(samlAttribute.AnyAttr); } }
/// <summary> /// Validate <c>AuthzDecisionStatement</c>. /// </summary> /// <remarks> /// [SAML2.0 standard] section 2.7.4 /// </remarks> /// <param name="statement">The statement.</param> private void ValidateAuthzDecisionStatement(AuthzDecisionStatement statement) { // This has type anyURI, and can be empty (special case in the standard), but not null. if (statement.Resource == null) { throw new Saml20FormatException("Resource attribute of AuthzDecisionStatement is REQUIRED"); } // If it is not empty, it MUST BE a valid URI if (statement.Resource.Length > 0 && !Uri.IsWellFormedUriString(statement.Resource, UriKind.Absolute)) { throw new Saml20FormatException("Resource attribute of AuthzDecisionStatement has a value which is not a wellformed absolute uri"); } // NOTE: Decision property validation is done implicitly be the deserializer since it is represented by an enumeration if (statement.Action == null || statement.Action.Length == 0) { throw new Saml20FormatException("At least one Action subelement must be present for an AuthzDecisionStatement element"); } foreach (var action in statement.Action) { // NOTE: [SAML2.0 standard] claims that the Namespace is [Optional], but according to the schema definition (and Geneva) // NOTE: it has use="required" if (!Saml20Utils.ValidateRequiredString(action.Namespace)) { throw new Saml20FormatException("Namespace attribute of Action element must contain at least one non-whitespace character"); } if (!Uri.IsWellFormedUriString(action.Namespace, UriKind.Absolute)) { throw new Saml20FormatException("Namespace attribute of Action element has a value which is not a wellformed absolute uri"); } } }
public void ValidateSubjectConfirmation(SubjectConfirmation subjectConfirmation) { if (subjectConfirmation.Method == SubjectConfirmation.BEARER_METHOD) { if (subjectConfirmation.SubjectConfirmationData == null) { throw new DKSaml20FormatException("The SAML profile requires that the bearer \"SubjectConfirmation\" element contains a \"SubjectConfirmationData\" element."); } if (!Saml20Utils.ValidateRequiredString(subjectConfirmation.SubjectConfirmationData.Recipient)) { throw new DKSaml20FormatException("The SAML profile requires that the \"SubjectConfirmationData\" element contains the \"Recipient\" attribute."); } if (!subjectConfirmation.SubjectConfirmationData.NotOnOrAfter.HasValue) { throw new DKSaml20FormatException("The SAML profile requires that the \"SubjectConfirmationData\" element contains the \"NotOnOrAfter\" attribute."); } if (subjectConfirmation.SubjectConfirmationData.NotBefore.HasValue) { throw new DKSaml20FormatException("The SAML profile disallows the use of the \"NotBefore\" attribute of the \"SubjectConfirmationData\" element."); } } }
/// <summary> /// Validates the XML any attributes. /// </summary> /// <param name="anyAttributes">Any attributes.</param> public void ValidateXmlAnyAttributes(XmlAttribute[] anyAttributes) { if (anyAttributes == null) { throw new ArgumentNullException("anyAttributes"); } if (anyAttributes.Length == 0) { return; } foreach (var attr in anyAttributes) { if (!Saml20Utils.ValidateRequiredString(attr.Prefix)) { throw new Saml20FormatException("Attribute extension xml attributes MUST BE namespace qualified"); } foreach (var samlns in Saml20Constants.SamlNamespaces) { if (attr.NamespaceURI == samlns) { throw new Saml20FormatException("Attribute extension xml attributes MUST NOT use a namespace reserved by SAML"); } } } }
/// <summary> /// Validates the <c>AuthnStatement</c>. /// </summary> /// <param name="authnStatement">The <c>AuthnStatement</c>.</param> /// <exception cref="SAML2.Profiles.DKSAML20.DKSaml20FormatException">The DK-SAML 2.0 profile requires that the <c>\AuthnStatement\</c> element contains the <c>\SessionIndex\</c> attribute.</exception> private void ValidateAuthnStatement(AuthnStatement authnStatement) { if (!Saml20Utils.ValidateRequiredString(authnStatement.SessionIndex)) { throw new DKSaml20FormatException("The DK-SAML 2.0 profile requires that the \"AuthnStatement\" element contains the \"SessionIndex\" attribute."); } }
public void ValidateSubjectConfirmation(SubjectConfirmation subjectConfirmation) { if (subjectConfirmation == null) { throw new ArgumentNullException("subjectConfirmation"); } if (!Saml20Utils.ValidateRequiredString(subjectConfirmation.Method)) { throw new Saml20FormatException("Method attribute of SubjectConfirmation MUST contain at least one non-whitespace character"); } if (!Uri.IsWellFormedUriString(subjectConfirmation.Method, UriKind.Absolute)) { throw new Saml20FormatException("SubjectConfirmation element has Method attribute which is not a wellformed absolute uri."); } if (subjectConfirmation.Method == Saml20Constants.SubjectConfirmationMethods.HolderOfKey) { KeyInfoValidator.ValidateKeyInfo(subjectConfirmation.SubjectConfirmationData); } if (subjectConfirmation.Item != null) { if (subjectConfirmation.Item is NameID) { NameIdValidator.ValidateNameID((NameID)subjectConfirmation.Item); } else if (subjectConfirmation.Item is EncryptedElement) { NameIdValidator.ValidateEncryptedID((EncryptedElement)subjectConfirmation.Item); } else { throw new Saml20FormatException(String.Format("Identifier of type {0} is not supported for SubjectConfirmation", subjectConfirmation.Item.GetType())); } } else if (subjectConfirmation.SubjectConfirmationData != null) { SubjectConfirmationDataValidator.ValidateSubjectConfirmationData(subjectConfirmation.SubjectConfirmationData); } }
/// <summary> /// Validates that all the required attributes are present on the assertion. /// Furthermore it validates validity of the Issuer element. /// </summary> /// <param name="assertion">The assertion.</param> private void ValidateAssertionAttributes(Assertion assertion) { // There must be a Version if (!Saml20Utils.ValidateRequiredString(assertion.Version)) { throw new Saml20FormatException("Assertion element must have the Version attribute set."); } // Version must be 2.0 if (assertion.Version != Saml20Constants.Version) { throw new Saml20FormatException("Wrong value of version attribute on Assertion element"); } // Assertion must have an ID if (!Saml20Utils.ValidateRequiredString(assertion.Id)) { throw new Saml20FormatException("Assertion element must have the ID attribute set."); } // Make sure that the ID elements is at least 128 bits in length (SAML2.0 std section 1.3.4) if (!Saml20Utils.ValidateIdString(assertion.Id)) { throw new Saml20FormatException("Assertion element must have an ID attribute with at least 16 characters (the equivalent of 128 bits)"); } // IssueInstant must be set. if (!assertion.IssueInstant.HasValue) { throw new Saml20FormatException("Assertion element must have the IssueInstant attribute set."); } // There must be an Issuer if (assertion.Issuer == null) { throw new Saml20FormatException("Assertion element must have an issuer element."); } // The Issuer element must be valid _nameIdValidator.ValidateNameId(assertion.Issuer); }
/// <summary> /// Validates the name ID. /// </summary> /// <param name="nameId">The name ID.</param> public void ValidateNameId(NameId nameId) { if (nameId == null) { throw new ArgumentNullException("nameId"); } if (string.IsNullOrEmpty(nameId.Format)) { return; } if (!Uri.IsWellFormedUriString(nameId.Format, UriKind.Absolute)) { throw new Saml20FormatException("NameID element has Format attribute which is not a wellformed absolute uri."); } // The processing rules from [SAML2.0 standard] section 8.3 are implemented here if (nameId.Format == Saml20Constants.NameIdentifierFormats.Email) { if (!Saml20Utils.ValidateRequiredString(nameId.Value)) { throw new Saml20FormatException("NameID with Email Format attribute MUST contain a Value that contains more than whitespace characters"); } try { new MailAddress(nameId.Value); } catch (FormatException fe) { throw new Saml20FormatException("Value of NameID is not a valid email address according to the IETF RFC 2822 specification", fe); } catch (IndexOutOfRangeException ie) { throw new Saml20FormatException("Value of NameID is not a valid email address according to the IETF RFC 2822 specification", ie); } } else if (nameId.Format == Saml20Constants.NameIdentifierFormats.X509SubjectName) { if (!Saml20Utils.ValidateRequiredString(nameId.Value)) { throw new Saml20FormatException("NameID with X509SubjectName Format attribute MUST contain a Value that contains more than whitespace characters"); } // TODO: Consider checking for correct encoding of the Value according to the // XML Signature Recommendation (http://www.w3.org/TR/xmldsig-core/) section 4.4.4 } else if (nameId.Format == Saml20Constants.NameIdentifierFormats.Windows) { // Required format is 'DomainName\UserName' but the domain name and the '\' are optional if (!Saml20Utils.ValidateRequiredString(nameId.Value)) { throw new Saml20FormatException("NameID with Windows Format attribute MUST contain a Value that contains more than whitespace characters"); } } else if (nameId.Format == Saml20Constants.NameIdentifierFormats.Kerberos) { // Required format is 'name[/instance]@REALM' if (!Saml20Utils.ValidateRequiredString(nameId.Value)) { throw new Saml20FormatException("NameID with Kerberos Format attribute MUST contain a Value that contains more than whitespace characters"); } if (nameId.Value.Length < 3) { throw new Saml20FormatException("NameID with Kerberos Format attribute MUST contain a Value with at least 3 characters"); } if (nameId.Value.IndexOf("@") < 0) { throw new Saml20FormatException("NameID with Kerberos Format attribute MUST contain a Value that contains a '@'"); } // TODO: Consider implementing the rules for 'name', 'instance' and 'REALM' found in IETF RFC 1510 (http://www.ietf.org/rfc/rfc1510.txt) here } else if (nameId.Format == Saml20Constants.NameIdentifierFormats.Entity) { if (!Saml20Utils.ValidateRequiredString(nameId.Value)) { throw new Saml20FormatException("NameID with Entity Format attribute MUST contain a Value that contains more than whitespace characters"); } if (nameId.Value.Length > 1024) { throw new Saml20FormatException("NameID with Entity Format attribute MUST have a Value that contains no more than 1024 characters"); } if (nameId.NameQualifier != null) { throw new Saml20FormatException("NameID with Entity Format attribute MUST NOT set the NameQualifier attribute"); } if (nameId.SPNameQualifier != null) { throw new Saml20FormatException("NameID with Entity Format attribute MUST NOT set the SPNameQualifier attribute"); } if (nameId.SPProvidedID != null) { throw new Saml20FormatException("NameID with Entity Format attribute MUST NOT set the SPProvidedID attribute"); } } else if (nameId.Format == Saml20Constants.NameIdentifierFormats.Persistent) { if (!Saml20Utils.ValidateRequiredString(nameId.Value)) { throw new Saml20FormatException("NameID with Persistent Format attribute MUST contain a Value that contains more than whitespace characters"); } if (nameId.Value.Length > 256) { throw new Saml20FormatException("NameID with Persistent Format attribute MUST have a Value that contains no more than 256 characters"); } } else if (nameId.Format == Saml20Constants.NameIdentifierFormats.Transient) { if (!Saml20Utils.ValidateRequiredString(nameId.Value)) { throw new Saml20FormatException("NameID with Transient Format attribute MUST contain a Value that contains more than whitespace characters"); } if (nameId.Value.Length > 256) { throw new Saml20FormatException("NameID with Transient Format attribute MUST have a Value that contains no more than 256 characters"); } if (!Saml20Utils.ValidateIdString(nameId.Value)) { throw new Saml20FormatException("NameID with Transient Format attribute MUST have a Value with at least 16 characters (the equivalent of 128 bits)"); } } }