s3:libads: Add a basic Windows SPN parser.
authorNoel Power <noel.power@suse.com>
Mon, 29 Jan 2018 17:51:15 +0000 (17:51 +0000)
committerAndreas Schneider <asn@cryptomilk.org>
Fri, 2 Mar 2018 13:07:14 +0000 (14:07 +0100)
(see https://social.technet.microsoft.com/wiki/contents/articles/717.service-principal-names-spns-setspn-syntax-setspn-exe.aspx)

Signed-off-by: Noel Power <noel.power@suse.com>
Reviewed-by: Jeremy Allison <jra@samba.org>
Reviewed-by: Andreas Schneider <asn@samba.org>
source3/libads/ads_proto.h
source3/libads/util.c

index 8e9a7690a0f24f3c9d2cebe97ecd5cd0fe2e4fee..a35f211c7d31390b07c3691684d90504f460336e 100644 (file)
@@ -193,4 +193,14 @@ void ndr_print_ads_saslwrap_struct(struct ndr_print *ndr,
 
 ADS_STATUS ads_change_trust_account_password(ADS_STRUCT *ads, char *host_principal);
 
+struct spn_struct {
+       const char *serviceclass;
+       const char *servicename;
+       const char *host;
+       int32_t port;
+};
+
+/* parse a windows style SPN, returns NULL if parsing fails */
+struct spn_struct *parse_spn(TALLOC_CTX *ctx, const char *srvprinc);
+
 #endif /* _LIBADS_ADS_PROTO_H_ */
index 14dbf86b21a935b9ca324d337a35de2dac25021f..68c7aede21e74aa1efd244f95dd05e928abfa2fa 100644 (file)
@@ -133,3 +133,106 @@ ADS_STATUS ads_change_trust_account_password(ADS_STRUCT *ads, char *host_princip
        return ADS_SUCCESS;
 }
 #endif
+
+/**
+* @brief Parses windows style SPN service/host:port/servicename
+*      serviceclass - A string that identifies the general class of service
+*            e.g. 'http'
+*      host - A netbios name or fully-qualified DNS name
+*      port - An optional TCP or UDP port number
+*      servicename - An optional distinguished name, GUID, DNS name or
+*                    DNS name of an SRV or MX record. (not needed for host
+*                    based services)
+*
+* @param[in]  ctx      - Talloc context.
+* @param[in]  srvprinc  - The service principal
+*
+* @return              - struct spn_struct containing the fields parsed or NULL
+*                        if srvprinc could not be parsed.
+*/
+struct spn_struct *parse_spn(TALLOC_CTX *ctx, const char *srvprinc)
+{
+       struct spn_struct * result = NULL;
+       char *tmp = NULL;
+       char *port_str = NULL;
+       char *host_str = NULL;
+
+       result = talloc_zero(ctx, struct spn_struct);
+       if (result == NULL) {
+               DBG_ERR("Out of memory\n");
+               return NULL;
+       }
+
+       result->serviceclass = talloc_strdup(result, srvprinc);
+       if (result->serviceclass == NULL) {
+               DBG_ERR("Out of memory\n");
+               return NULL;
+       }
+       result->port = -1;
+
+       tmp = strchr_m(result->serviceclass, '/');
+       if (tmp == NULL) {
+               /* illegal */
+               DBG_ERR("Failed to parse spn %s, no host definition\n",
+                       srvprinc);
+               TALLOC_FREE(result);
+               goto out;
+       }
+
+       /* terminate service principal */
+       *tmp = '\0';
+       tmp++;
+       host_str = tmp;
+
+       tmp = strchr_m(host_str, ':');
+       if (tmp != NULL) {
+               *tmp  = '\0';
+               tmp++;
+               port_str = tmp;
+       } else {
+               tmp = host_str;
+       }
+
+       tmp = strchr_m(tmp, '/');
+       if (tmp != NULL) {
+               *tmp  = '\0';
+               tmp++;
+               result->servicename = tmp;
+       }
+
+       if (strlen(host_str) == 0) {
+               /* illegal */
+               DBG_ERR("Failed to parse spn %s, illegal host definition\n",
+                       srvprinc);
+               TALLOC_FREE(result);
+               goto out;
+       }
+       result->host = host_str;
+
+       if (result->servicename != NULL && (strlen(result->servicename) == 0)) {
+               DBG_ERR("Failed to parse spn %s, empty servicename "
+                       "definition\n", srvprinc);
+               TALLOC_FREE(result);
+               goto out;
+       }
+       if (port_str != NULL) {
+               if (strlen(port_str) == 0) {
+                       DBG_ERR("Failed to parse spn %s, empty port "
+                               "definition\n", srvprinc);
+                       TALLOC_FREE(result);
+                       goto out;
+               }
+               result->port = (int32_t)strtol(port_str, NULL, 10);
+               if (result->port <= 0
+                   || result->port > 65535
+                   || errno == ERANGE) {
+                       DBG_ERR("Failed to parse spn %s, port number "
+                               "convertion failed\n", srvprinc);
+                       errno = 0;
+                       TALLOC_FREE(result);
+                       goto out;
+               }
+       }
+out:
+       return result;
+}