github.com/Venafi/vcert/v5@v5.10.2/pkg/venafi/cloud/cloud.go (about) 1 /* 2 * Copyright 2018 Venafi, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package cloud 18 19 import ( 20 "bytes" 21 // nolint:gosec // we only use it for getting the certificate thumbprint / fingerprint 22 //TODO: although doesn't oppose a risk, we need to figure out a better to do this process so we can remove this library 23 "crypto/sha1" 24 "crypto/tls" 25 "encoding/json" 26 "fmt" 27 "io" 28 "log" 29 "net" 30 "net/http" 31 "net/url" 32 "sort" 33 "strconv" 34 "strings" 35 "time" 36 37 "github.com/go-http-utils/headers" 38 39 "github.com/Venafi/vcert/v5/pkg/certificate" 40 "github.com/Venafi/vcert/v5/pkg/endpoint" 41 "github.com/Venafi/vcert/v5/pkg/policy" 42 "github.com/Venafi/vcert/v5/pkg/util" 43 "github.com/Venafi/vcert/v5/pkg/verror" 44 ) 45 46 type apiKey struct { 47 Key string `json:"key,omitempty"` 48 UserID string `json:"userId,omitempty"` 49 Username string `json:"username,omitempty"` 50 CompanyID string `json:"companyId,omitempty"` 51 APITypes []string `json:"apitypes,omitempty"` 52 APIVersion string `json:"apiVersion,omitempty"` 53 APIKeyStatus string `json:"apiKeyStatus,omitempty"` 54 CreationDateString string `json:"creationDate,omitempty"` 55 CreationDate time.Time `json:"-"` 56 ValidityStartDateString string `json:"validityStartDate,omitempty"` 57 ValidityStartDate time.Time `json:"-"` 58 ValidityEndDateString string `json:"validityEndDate,omitempty"` 59 ValidityEndDate time.Time `json:"-"` 60 } 61 62 type userDetails struct { 63 User *user `json:"user,omitempty"` 64 Company *company `json:"company,omitempty"` 65 APIKey *apiKey `json:"apiKey,omitempty"` 66 } 67 68 type OwnerType int64 69 70 const ( 71 UserType OwnerType = iota 72 TeamType 73 ) 74 75 func (o OwnerType) String() string { 76 switch o { 77 case UserType: 78 return "USER" 79 case TeamType: 80 return "TEAM" 81 } 82 return "unknown" 83 } 84 85 type certificateRequestResponse struct { 86 CertificateRequests []certificateRequestResponseData `json:"certificateRequests,omitempty"` 87 } 88 89 type certificateRequestResponseData struct { 90 ID string `json:"id,omitempty"` 91 ApplicationId string `json:"applicationId,omitempty"` 92 TemplateId string `json:"certificateIssuingTemplateId,omitempty"` 93 Status string `json:"status,omitempty"` 94 SubjectDN string `json:"subjectDN,omitempty"` 95 CreationDateString string `json:"creationDate,omitempty"` 96 CreationDate time.Time `json:"-"` 97 CertificateIds []string `json:"certificateIds,omitempty"` 98 } 99 100 type certificateRequestClientInfo struct { 101 Type string `json:"type"` 102 Identifier string `json:"identifier"` 103 } 104 105 type certificateRetireResponse struct { 106 Count int `count:"id,omitempty"` 107 Certificates []Certificate `json:"certificates,omitempty"` 108 } 109 110 type certificateRequest struct { 111 CSR string `json:"certificateSigningRequest,omitempty"` 112 ApplicationId string `json:"applicationId,omitempty"` 113 TemplateId string `json:"certificateIssuingTemplateId,omitempty"` 114 CertificateOwnerUserId string `json:"certificateOwnerUserId,omitempty"` 115 ExistingCertificateId string `json:"existingCertificateId,omitempty"` 116 ApiClientInformation certificateRequestClientInfo `json:"apiClientInformation,omitempty"` 117 CertificateUsageMetadata []certificateUsageMetadata `json:"certificateUsageMetadata,omitempty"` 118 ReuseCSR bool `json:"reuseCSR,omitempty"` 119 ValidityPeriod string `json:"validityPeriod,omitempty"` 120 IsVaaSGenerated bool `json:"isVaaSGenerated,omitempty"` 121 CsrAttributes CsrAttributes `json:"csrAttributes,omitempty"` 122 ApplicationServerTypeId string `json:"applicationServerTypeId,omitempty"` 123 } 124 125 type certificateRetireRequest struct { 126 CertificateIds []string `json:"certificateIds,omitempty"` 127 AddToBlocklist bool `json:"addToBlocklist,omitempty"` 128 } 129 130 type CsrAttributes struct { 131 CommonName *string `json:"commonName,omitempty"` 132 Organization *string `json:"organization,omitempty"` 133 OrganizationalUnits []string `json:"organizationalUnits,omitempty"` 134 Locality *string `json:"locality,omitempty"` 135 State *string `json:"state,omitempty"` 136 Country *string `json:"country,omitempty"` 137 SubjectAlternativeNamesByType *SubjectAlternativeNamesByType `json:"subjectAlternativeNamesByType,omitempty"` 138 KeyTypeParameters *KeyTypeParameters `json:"keyTypeParameters,omitempty"` 139 } 140 141 type KeyTypeParameters struct { 142 KeyType string `json:"keyType,omitempty"` 143 KeyLength *int `json:"keyLength,omitempty"` 144 KeyCurve *string `json:"keyCurve,omitempty"` 145 } 146 147 type SubjectAlternativeNamesByType struct { 148 DnsNames []string `json:"dnsNames,omitempty"` 149 IpAddresses []string `json:"ipAddresses,omitempty"` 150 Rfc822Names []string `json:"rfc822Names,omitempty"` 151 UniformResourceIdentifiers []string `json:"uniformResourceIdentifiers,omitempty"` 152 } 153 154 type KeyStoreRequest struct { 155 ExportFormat string `json:"exportFormat,omitempty"` 156 EncryptedPrivateKeyPassphrase string `json:"encryptedPrivateKeyPassphrase"` 157 EncryptedKeystorePassphrase string `json:"encryptedKeystorePassphrase"` 158 CertificateLabel string `json:"certificateLabel"` 159 } 160 161 type EdgeEncryptionKey struct { 162 Key string `json:"key,omitempty"` 163 } 164 165 type certificateStatus struct { 166 Id string `json:"id,omitempty"` 167 CertificateIdsList []string `json:"certificateIds,omitempty"` 168 ApplicationId string `json:"applicationId,omitempty"` 169 TemplateId string `json:"certificateIssuingTemplateId,omitempty"` 170 Status string `json:"status,omitempty"` 171 ErrorInformation CertificateStatusErrorInformation `json:"errorInformation,omitempty"` 172 CreationDate string `json:"creationDate,omitempty"` 173 ModificationDate string `json:"modificationDate,omitempty"` 174 CertificateSigningRequest string `json:"certificateSigningRequest,omitempty"` 175 SubjectDN string `json:"subjectDN,omitempty"` 176 } 177 178 type CertificateStatusErrorInformation struct { 179 Type string `json:"type,omitempty"` 180 Code int `json:"code,omitempty"` 181 Message string `json:"message,omitempty"` 182 Args []string `json:"args,omitempty"` 183 } 184 185 type apiClientInformation struct { 186 Type string `json:"type"` 187 Identifier string `json:"identifier"` 188 } 189 190 type certificateUsageMetadata struct { 191 AppName string `json:"appName,omitempty"` 192 NodeName string `json:"nodeName,omitempty"` 193 AutomationMetadata string `json:"automationMetadata,omitempty"` 194 } 195 196 type importRequest struct { 197 Certificates []importRequestCertInfo `json:"certificates"` 198 } 199 200 type importRequestCertInfo struct { 201 Certificate string `json:"certificate"` 202 IssuerCertificates []string `json:"issuerCertificates,omitempty"` 203 ApplicationIds []string `json:"applicationIds"` 204 ApiClientInformation apiClientInformation `json:"apiClientInformation,omitempty"` 205 CertificateUsageMetadata []certificateUsageMetadata `json:"certificateUsageMetadata,omitempty"` 206 } 207 208 type importResponseCertInfo struct { 209 Id string `json:"id"` 210 ManagedCertificateId string `json:"managedCertificateId"` 211 CompanyId string `json:"companyId"` 212 Fingerprint string `json:"fingerprint"` 213 CertificateSource string `json:"certificateSource"` 214 OwnerUserId string `json:"ownerUserId"` 215 IssuanceZoneId string `json:"issuanceZoneId"` 216 ValidityStartDateString string `json:"validityStartDate"` 217 ValidityStartDate time.Time `json:"-"` 218 ValidityEndDateString string `json:"validityEndDate"` 219 ValidityEndDate time.Time `json:"-"` 220 ApiClientInformation apiClientInformation `json:"apiClientInformation,omitempty"` 221 } 222 223 type importResponse struct { 224 CertificateInformations []importResponseCertInfo `json:"certificateInformations"` 225 } 226 227 type ApplicationDetails struct { 228 ApplicationId string `json:"id,omitempty"` 229 CitAliasToIdMap map[string]string `json:"certificateIssuingTemplateAliasIdMap,omitempty"` 230 CompanyId string `json:"companyId,omitempty"` 231 Name string `json:"name,omitempty"` 232 Description string `json:"description,omitempty"` 233 OwnerIdType []policy.OwnerIdType `json:"ownerIdsAndTypes,omitempty"` 234 InternalFqDns []string `json:"internalFqDns,omitempty"` 235 ExternalIpRanges []string `json:"externalIpRanges,omitempty"` 236 InternalIpRanges []string `json:"internalIpRanges,omitempty"` 237 InternalPorts []string `json:"internalPorts,omitempty"` 238 FullyQualifiedDomainNames []string `json:"fullyQualifiedDomainNames,omitempty"` 239 IpRanges []string `json:"ipRanges,omitempty"` 240 Ports []string `json:"ports,omitempty"` 241 FqDns []string `json:"fqDns,omitempty"` 242 } 243 244 // GenerateRequest generates a CertificateRequest based on the zone configuration, and returns the request along with the private key. 245 func (c *Connector) GenerateRequest(config *endpoint.ZoneConfiguration, req *certificate.Request) (err error) { 246 switch req.CsrOrigin { 247 case certificate.LocalGeneratedCSR: 248 if config == nil { 249 config, err = c.ReadZoneConfiguration() 250 if err != nil { 251 return fmt.Errorf("could not read zone configuration: %w", err) 252 } 253 } 254 config.UpdateCertificateRequest(req) 255 if err := req.GeneratePrivateKey(); err != nil { 256 return err 257 } 258 err = req.GenerateCSR() 259 return 260 case certificate.UserProvidedCSR: 261 if len(req.GetCSR()) == 0 { 262 return fmt.Errorf("%w: CSR was supposed to be provided by user, but it's empty", verror.UserDataError) 263 } 264 return nil 265 266 case certificate.ServiceGeneratedCSR: 267 if req.KeyType == certificate.KeyTypeED25519 { 268 return fmt.Errorf("%w: ED25519 keys are not yet supported for Service Generated CSR", verror.UserDataError) 269 } 270 return nil 271 272 default: 273 return fmt.Errorf("%w: unrecognised req.CsrOrigin %v", verror.UserDataError, req.CsrOrigin) 274 } 275 } 276 277 func (c *Connector) getURL(resource urlResource) string { 278 return fmt.Sprintf("%s%s", c.baseURL, resource) 279 } 280 281 func (c *Connector) getHTTPClient() *http.Client { 282 if c.client != nil { 283 return c.client 284 } 285 var netTransport = &http.Transport{ 286 Proxy: http.ProxyFromEnvironment, 287 DialContext: (&net.Dialer{ 288 Timeout: 30 * time.Second, 289 KeepAlive: 30 * time.Second, 290 }).DialContext, 291 MaxIdleConns: 100, 292 IdleConnTimeout: 90 * time.Second, 293 TLSHandshakeTimeout: 10 * time.Second, 294 ExpectContinueTimeout: 1 * time.Second, 295 } 296 tlsConfig := http.DefaultTransport.(*http.Transport).TLSClientConfig 297 /* #nosec */ 298 if c.trust != nil { 299 if tlsConfig == nil { 300 tlsConfig = &tls.Config{ 301 MinVersion: tls.VersionTLS12, 302 } 303 } else { 304 tlsConfig = tlsConfig.Clone() 305 } 306 tlsConfig.RootCAs = c.trust 307 netTransport.TLSClientConfig = tlsConfig 308 } 309 310 c.client = &http.Client{ 311 Timeout: time.Second * 30, 312 Transport: netTransport, 313 } 314 return c.client 315 } 316 317 func (c *Connector) request(method string, url string, data interface{}, authNotRequired ...bool) (statusCode int, statusText string, body []byte, err error) { 318 if (c.accessToken == "" && c.user == nil) || (c.user != nil && c.user.Company == nil) { 319 if !(len(authNotRequired) == 1 && authNotRequired[0]) { 320 err = fmt.Errorf("%w: must be autheticated to make requests to TLSPC API", verror.VcertError) 321 return 322 } 323 } 324 325 var payload io.Reader 326 var b []byte 327 if method == http.MethodPost || method == http.MethodPut { 328 b, _ = json.Marshal(data) 329 payload = bytes.NewReader(b) 330 } 331 332 r, err := http.NewRequest(method, url, payload) 333 if err != nil { 334 err = fmt.Errorf("%w: %v", verror.VcertError, err) 335 return 336 } 337 338 r.Header.Set(headers.UserAgent, c.userAgent) 339 if c.accessToken != "" { 340 r.Header.Add(headers.Authorization, fmt.Sprintf("%s %s", util.OauthTokenType, c.accessToken)) 341 } else if c.apiKey != "" { 342 r.Header.Add(util.HeaderTpplApikey, c.apiKey) 343 } 344 345 if method == http.MethodPost || method == http.MethodPut { 346 r.Header.Add(headers.Accept, "application/json") 347 r.Header.Add(headers.ContentType, "application/json") 348 } else { 349 r.Header.Add(headers.Accept, "*/*") 350 } 351 r.Header.Add(headers.CacheControl, "no-cache") 352 353 var httpClient = c.getHTTPClient() 354 355 res, err := httpClient.Do(r) 356 if err != nil { 357 err = fmt.Errorf("%w: %v", verror.ServerUnavailableError, err) 358 return 359 } 360 statusCode = res.StatusCode 361 statusText = res.Status 362 363 defer res.Body.Close() 364 body, err = io.ReadAll(res.Body) 365 if err != nil { 366 err = fmt.Errorf("%w: %v", verror.ServerError, err) 367 } 368 369 if c.verbose { 370 log.Printf("Got %s status for %s %s\n", statusText, method, url) 371 } 372 return 373 } 374 375 func parseUserDetailsResult(expectedStatusCode int, httpStatusCode int, httpStatus string, body []byte) (*userDetails, error) { 376 if httpStatusCode == expectedStatusCode { 377 return parseJSON[userDetails](body, verror.ServerError) 378 } 379 respErrors, err := parseResponseErrors(body) 380 if err != nil { 381 // Parsing the error failed, return the original error 382 bodyText := strings.TrimSpace(string(body)) 383 if bodyText == "" { 384 return nil, fmt.Errorf("%w: %s", verror.ServerError, httpStatus) 385 } 386 387 return nil, fmt.Errorf("%w: %s, %s", verror.ServerError, httpStatus, bodyText) 388 } 389 respError := fmt.Sprintf("unexpected status code on Venafi Cloud registration. Status: %s\n", httpStatus) 390 for _, e := range respErrors { 391 respError += fmt.Sprintf("Error Code: %d Error: %s\n", e.Code, e.Message) 392 } 393 return nil, fmt.Errorf("%w: %v", verror.ServerError, respError) 394 } 395 396 func parseUserDetailsResultFromPOST(httpStatusCode int, httpStatus string, body []byte) (*userDetails, error) { 397 if httpStatusCode == http.StatusCreated || httpStatusCode == http.StatusAccepted { 398 return parseJSON[userDetails](body, verror.ServerError) 399 } 400 respErrors, err := parseResponseErrors(body) 401 if err != nil { 402 return nil, err // parseResponseErrors always return verror.ServerError 403 } 404 respError := fmt.Sprintf("unexpected status code on Venafi Cloud registration. Status: %s\n", httpStatus) 405 for _, e := range respErrors { 406 respError += fmt.Sprintf("Error Code: %d Error: %s\n", e.Code, e.Message) 407 } 408 return nil, fmt.Errorf("%w: %v", verror.ServerError, respError) 409 } 410 411 func parseJSON[T any](b []byte, errorMessage error) (*T, error) { 412 var data T 413 err := json.Unmarshal(b, &data) 414 if err != nil { 415 return nil, fmt.Errorf("%w: %v", errorMessage, err) 416 } 417 return &data, nil 418 } 419 420 func parseUserByIdResult(expectedStatusCode int, httpStatusCode int, httpStatus string, body []byte) (*user, error) { 421 if httpStatusCode == expectedStatusCode { 422 return parseJSON[user](body, verror.ServerError) 423 } 424 respErrors, err := parseResponseErrors(body) 425 if err != nil { 426 return nil, err // parseResponseErrors always return verror.ServerError 427 } 428 respError := fmt.Sprintf("unexpected status code on retrieval of user by ID. Status: %s\n", httpStatus) 429 for _, e := range respErrors { 430 respError += fmt.Sprintf("Error Code: %d Error: %s\n", e.Code, e.Message) 431 } 432 return nil, fmt.Errorf("%w: %v", verror.ServerError, respError) 433 } 434 435 func parseUsersByNameResult(expectedStatusCode int, httpStatusCode int, httpStatus string, body []byte) (*users, error) { 436 if httpStatusCode == expectedStatusCode { 437 return parseJSON[users](body, verror.ServerError) 438 } 439 respErrors, err := parseResponseErrors(body) 440 if err != nil { 441 return nil, err // parseResponseErrors always return verror.ServerError 442 } 443 respError := fmt.Sprintf("unexpected status code on retrieval of users by name. Status: %s\n", httpStatus) 444 for _, e := range respErrors { 445 respError += fmt.Sprintf("Error Code: %d Error: %s\n", e.Code, e.Message) 446 } 447 return nil, fmt.Errorf("%w: %v", verror.ServerError, respError) 448 } 449 450 func parseCertByIdResult(expectedStatusCode int, httpStatusCode int, httpStatus string, body []byte) (*VenafiCertificate, error) { 451 if httpStatusCode == expectedStatusCode { 452 return parseJSON[VenafiCertificate](body, verror.ServerError) 453 } 454 respErrors, err := parseResponseErrors(body) 455 if err != nil { 456 return nil, err // parseResponseErrors always return verror.ServerError 457 } 458 respError := fmt.Sprintf("unexpected status code on retrieval of certificate by ID. Status: %s\n", httpStatus) 459 for _, e := range respErrors { 460 respError += fmt.Sprintf("Error Code: %d Error: %s\n", e.Code, e.Message) 461 } 462 return nil, fmt.Errorf("%w: %v", verror.ServerError, respError) 463 } 464 465 func parseTeamsResult(expectedStatusCode int, httpStatusCode int, httpStatus string, body []byte) (*teams, error) { 466 if httpStatusCode == expectedStatusCode { 467 return parseJSON[teams](body, verror.ServerError) 468 } 469 respErrors, err := parseResponseErrors(body) 470 if err != nil { 471 return nil, err // parseResponseErrors always return verror.ServerError 472 } 473 respError := fmt.Sprintf("unexpected status code on retrieval of teams. Status: %s\n", httpStatus) 474 for _, e := range respErrors { 475 respError += fmt.Sprintf("Error Code: %d Error: %s\n", e.Code, e.Message) 476 } 477 return nil, fmt.Errorf("%w: %v", verror.ServerError, respError) 478 } 479 480 func parseZoneConfigurationResult(httpStatusCode int, httpStatus string, body []byte) (*zone, error) { 481 switch httpStatusCode { 482 case http.StatusOK: 483 return parseJSON[zone](body, verror.ServerError) 484 case http.StatusBadRequest, http.StatusNotFound: 485 return nil, verror.ZoneNotFoundError 486 default: 487 respErrors, err := parseResponseErrors(body) 488 if err != nil { 489 return nil, err 490 } 491 492 respError := fmt.Sprintf("Unexpected status code on Venafi Cloud zone read. Status: %s\n", httpStatus) 493 for _, e := range respErrors { 494 if e.Code == 10051 { 495 return nil, verror.ZoneNotFoundError 496 } 497 respError += fmt.Sprintf("Error Code: %d Error: %s\n", e.Code, e.Message) 498 } 499 return nil, fmt.Errorf("%w: %v", verror.ServerError, respError) 500 } 501 } 502 503 func parseCertificateTemplateResult(httpStatusCode int, httpStatus string, body []byte) (*certificateTemplate, error) { 504 switch httpStatusCode { 505 case http.StatusOK: 506 return parseJSON[certificateTemplate](body, verror.ServerError) 507 case http.StatusBadRequest: 508 return nil, verror.ZoneNotFoundError 509 case http.StatusUnauthorized: 510 return nil, verror.UnauthorizedError 511 default: 512 respErrors, err := parseResponseErrors(body) 513 if err != nil { 514 return nil, err 515 } 516 517 respError := fmt.Sprintf("Unexpected status code on Venafi Cloud zone read. Status: %s\n", httpStatus) 518 for _, e := range respErrors { 519 if e.Code == 10051 { 520 return nil, verror.ZoneNotFoundError 521 } 522 respError += fmt.Sprintf("Error Code: %d Error: %s\n", e.Code, e.Message) 523 } 524 return nil, fmt.Errorf("%w: %v", verror.ServerError, respError) 525 } 526 } 527 528 func parseCertificateRequestResult(httpStatusCode int, httpStatus string, body []byte) (*certificateRequestResponse, error) { 529 switch httpStatusCode { 530 case http.StatusCreated: 531 return parseJSON[certificateRequestResponse](body, verror.ServerError) 532 default: 533 respErrors, err := parseResponseErrors(body) 534 if err != nil { 535 return nil, err 536 } 537 538 respError := fmt.Sprintf("Unexpected status code on Venafi Cloud zone read. Status: %s\n", httpStatus) 539 for _, e := range respErrors { 540 respError += fmt.Sprintf("Error Code: %d Error: %s\n", e.Code, e.Message) 541 } 542 return nil, fmt.Errorf("%w: %v", verror.ServerError, respError) 543 } 544 } 545 546 func checkCertificateRetireResults(httpStatusCode int, httpStatus string, body []byte) error { 547 switch httpStatusCode { 548 case 200: 549 resp, err := parseJSON[certificateRetireResponse](body, verror.ServerError) 550 if err != nil { 551 return err 552 } else if resp.Count == 0 { 553 return fmt.Errorf("Invalid thumbprint or certificate ID. No certificates were retired") 554 } else { 555 return nil 556 } 557 default: 558 respErrors, err := parseResponseErrors(body) 559 if err != nil { 560 return err 561 } 562 563 respError := fmt.Sprintf("Unexpected status code on Venafi Cloud zone read. Status: %s\n", httpStatus) 564 for _, e := range respErrors { 565 respError += fmt.Sprintf("Error Code: %d Error: %s\n", e.Code, e.Message) 566 } 567 return fmt.Errorf("%w: %v", verror.ServerError, respError) 568 } 569 } 570 571 func newPEMCollectionFromResponse(data []byte, chainOrder certificate.ChainOption) (*certificate.PEMCollection, error) { 572 return certificate.PEMCollectionFromBytes(data, chainOrder) 573 } 574 575 func certThumbprint(asn1 []byte) string { 576 // nolint:gosec // we only use it for getting the certificate thumbprint / fingerprint 577 h := sha1.Sum(asn1) // TODO: although doesn't oppose a risk, we need to figure out a better to do this process 578 return strings.ToUpper(fmt.Sprintf("%x", h)) 579 } 580 581 func parseApplicationDetailsResult(httpStatusCode int, httpStatus string, body []byte) (*ApplicationDetails, error) { 582 switch httpStatusCode { 583 case http.StatusOK: 584 return parseJSON[ApplicationDetails](body, verror.ServerError) 585 case http.StatusBadRequest: 586 return nil, verror.ApplicationNotFoundError 587 case http.StatusUnauthorized: 588 return nil, fmt.Errorf("%w: %s", verror.ServerError, httpStatus) 589 default: 590 respErrors, err := parseResponseErrors(body) 591 if err != nil { 592 return nil, err 593 } 594 595 respError := fmt.Sprintf("Unexpected status code on Venafi Cloud application read. Status: %s\n", httpStatus) 596 for _, e := range respErrors { 597 if e.Code == 10051 { 598 return nil, verror.ApplicationNotFoundError 599 } 600 respError += fmt.Sprintf("Error Code: %d Error: %s\n", e.Code, e.Message) 601 } 602 return nil, fmt.Errorf("%w: %v", verror.ServerError, respError) 603 } 604 } 605 606 type cloudZone struct { 607 zone string 608 appName string 609 templateAlias string 610 } 611 612 func (z cloudZone) String() string { 613 return z.zone 614 } 615 616 func (z *cloudZone) getApplicationName() string { 617 if z.appName == "" { 618 err := z.parseZone() 619 if err != nil { 620 return "" 621 } 622 } 623 return z.appName 624 } 625 626 func (z *cloudZone) getTemplateAlias() string { 627 if z.templateAlias == "" { 628 err := z.parseZone() 629 if err != nil { 630 return "" 631 } 632 } 633 return z.templateAlias 634 } 635 636 func (z *cloudZone) parseZone() error { 637 if z.zone == "" { 638 return fmt.Errorf("zone not specified") 639 } 640 641 segments := strings.Split(z.zone, "\\") 642 if len(segments) != 2 { 643 return fmt.Errorf("invalid zone format") 644 } 645 646 z.appName = segments[0] 647 z.templateAlias = segments[1] 648 649 return nil 650 } 651 652 func createAppUpdateRequest(applicationDetails *ApplicationDetails) policy.Application { 653 request := policy.Application{ 654 OwnerIdsAndTypes: applicationDetails.OwnerIdType, 655 Name: applicationDetails.Name, 656 Description: applicationDetails.Description, 657 Fqdns: applicationDetails.FqDns, 658 InternalFqdns: applicationDetails.InternalFqDns, 659 InternalIpRanges: applicationDetails.InternalIpRanges, 660 ExternalIpRanges: applicationDetails.ExternalIpRanges, 661 InternalPorts: applicationDetails.InternalPorts, 662 FullyQualifiedDomainNames: applicationDetails.FullyQualifiedDomainNames, 663 IpRanges: applicationDetails.IpRanges, 664 Ports: applicationDetails.Ports, 665 CertificateIssuingTemplateAliasIdMap: applicationDetails.CitAliasToIdMap, 666 } 667 668 return request 669 } 670 671 func getSAN(p *policy.Policy) *policy.SubjectAltNames { 672 if p == nil || p.SubjectAltNames == nil { 673 san := policy.SubjectAltNames{} 674 p.SubjectAltNames = &san 675 return &san 676 } 677 return p.SubjectAltNames 678 } 679 680 func buildPolicySpecification(cit *certificateTemplate, info *policy.CertificateAuthorityInfo, removeRegex bool) *policy.PolicySpecification { 681 if cit == nil { 682 return nil 683 } 684 685 var ps policy.PolicySpecification 686 687 var pol policy.Policy 688 689 if len(cit.SubjectCNRegexes) > 0 { 690 if removeRegex { 691 pol.Domains = policy.RemoveRegex(cit.SubjectCNRegexes) 692 } else { 693 pol.Domains = cit.SubjectCNRegexes 694 } 695 } 696 697 wildCard := isWildCard(cit.SubjectCNRegexes) 698 pol.WildcardAllowed = &wildCard 699 700 if len(cit.SANRegexes) > 0 { 701 subjectAlt := getSAN(&pol) 702 subjectAlt.DnsAllowed = util.GetBooleanRef(true) 703 } 704 705 if len(cit.SanRfc822NameRegexes) > 0 { 706 subjectAlt := getSAN(&pol) 707 subjectAlt.EmailAllowed = util.GetBooleanRef(true) 708 } 709 710 if len(cit.SanUniformResourceIdentifierRegexes) > 0 { 711 subjectAlt := getSAN(&pol) 712 protocols := make([]string, 0) 713 for _, val := range cit.SanUniformResourceIdentifierRegexes { 714 index := strings.Index(val, ")://") 715 subStr := val[1:index] 716 currProtocols := strings.Split(subStr, "|") 717 for _, currentProtocol := range currProtocols { 718 if len(protocols) == 0 { 719 protocols = append(protocols, currentProtocol) 720 } else { 721 if !contains(protocols, currentProtocol) { 722 protocols = append(protocols, currentProtocol) 723 } 724 } 725 } 726 } 727 subjectAlt.UriProtocols = protocols 728 subjectAlt.UriAllowed = util.GetBooleanRef(true) 729 } 730 731 if len(cit.SanIpAddressRegexes) > 0 { 732 subjectAlt := getSAN(&pol) 733 subjectAlt.IpAllowed = util.GetBooleanRef(true) 734 } 735 736 // ps.Policy.WildcardAllowed is pending. 737 if cit.ValidityPeriod != "" { 738 //they have the format P#D 739 days := cit.ValidityPeriod[1 : len(cit.ValidityPeriod)-1] 740 intDays, _ := strconv.ParseInt(days, 10, 32) 741 //ok we have a 32 bits int but we need to convert it just into a "int" 742 intVal := int(intDays) 743 pol.MaxValidDays = &intVal 744 } 745 if info != nil { 746 ca := fmt.Sprint(info.CAType, "\\", info.CAAccountKey, "\\", info.VendorProductName) 747 pol.CertificateAuthority = &ca 748 } 749 750 //subject. 751 var subject policy.Subject 752 753 if len(cit.SubjectORegexes) > 0 { 754 subject.Orgs = cit.SubjectORegexes 755 } else if cit.SubjectORegexes == nil { 756 subject.Orgs = []string{""} 757 } 758 759 if len(cit.SubjectOURegexes) > 0 { 760 subject.OrgUnits = cit.SubjectOURegexes 761 } else if cit.SubjectOURegexes == nil { 762 subject.OrgUnits = []string{""} 763 } 764 765 if len(cit.SubjectLRegexes) > 0 { 766 subject.Localities = cit.SubjectLRegexes 767 } else if cit.SubjectLRegexes == nil { 768 subject.Localities = []string{""} 769 } 770 771 if len(cit.SubjectSTRegexes) > 0 { 772 subject.States = cit.SubjectSTRegexes 773 } else if cit.SubjectSTRegexes == nil { 774 subject.States = []string{""} 775 } 776 777 if len(cit.SubjectCValues) > 0 { 778 subject.Countries = cit.SubjectCValues 779 } else if cit.SubjectCValues == nil { 780 subject.Countries = []string{""} 781 } 782 783 pol.Subject = &subject 784 785 //key pair 786 var keyPair policy.KeyPair 787 shouldCreateKeyPair := false 788 if len(cit.KeyTypes) > 0 { 789 var keyTypes []string 790 var keySizes []int 791 var ellipticCurves []string 792 793 for _, allowedKT := range cit.KeyTypes { 794 keyType := string(allowedKT.KeyType) 795 keyLengths := allowedKT.KeyLengths 796 ecKeys := allowedKT.KeyCurves 797 798 keyTypes = append(keyTypes, keyType) 799 800 if len(keyLengths) > 0 { 801 keySizes = append(keySizes, keyLengths...) 802 } 803 804 if len(ecKeys) > 0 { 805 ellipticCurves = append(ellipticCurves, ecKeys...) 806 } 807 808 } 809 shouldCreateKeyPair = true 810 keyPair.KeyTypes = keyTypes 811 if len(keySizes) > 0 { 812 keyPair.RsaKeySizes = keySizes 813 } 814 815 if len(ellipticCurves) > 0 { 816 keyPair.EllipticCurves = ellipticCurves 817 } 818 } 819 820 if cit.KeyGeneratedByVenafiAllowed && cit.CsrUploadAllowed { 821 keyPair.ServiceGenerated = nil 822 } else if cit.KeyGeneratedByVenafiAllowed { 823 keyPair.ServiceGenerated = &cit.KeyGeneratedByVenafiAllowed 824 shouldCreateKeyPair = true 825 } else if cit.CsrUploadAllowed { 826 falseVal := false 827 keyPair.ServiceGenerated = &falseVal 828 shouldCreateKeyPair = true 829 } 830 831 if shouldCreateKeyPair { 832 pol.KeyPair = &keyPair 833 pol.KeyPair.ReuseAllowed = &cit.KeyReuse 834 } 835 836 ps.Policy = &pol 837 838 //build defaults. 839 var defaultSub policy.DefaultSubject 840 shouldCreateDeFaultSub := false 841 if cit.RecommendedSettings.SubjectOValue != "" { 842 defaultSub.Org = &cit.RecommendedSettings.SubjectOValue 843 shouldCreateDeFaultSub = true 844 } 845 846 if cit.RecommendedSettings.SubjectOUValue != "" { 847 defaultSub.OrgUnits = []string{cit.RecommendedSettings.SubjectOUValue} 848 shouldCreateDeFaultSub = true 849 } 850 851 if cit.RecommendedSettings.SubjectCValue != "" { 852 defaultSub.Country = &cit.RecommendedSettings.SubjectCValue 853 shouldCreateDeFaultSub = true 854 } 855 856 if cit.RecommendedSettings.SubjectSTValue != "" { 857 defaultSub.State = &cit.RecommendedSettings.SubjectSTValue 858 shouldCreateDeFaultSub = true 859 } 860 861 if cit.RecommendedSettings.SubjectLValue != "" { 862 defaultSub.Locality = &cit.RecommendedSettings.SubjectLValue 863 shouldCreateDeFaultSub = true 864 } 865 866 if shouldCreateDeFaultSub { 867 if ps.Default == nil { 868 ps.Default = &policy.Default{} 869 } 870 ps.Default.Subject = &defaultSub 871 } 872 873 //default key type 874 var defaultKP policy.DefaultKeyPair 875 shouldCreateDefaultKeyPAir := false 876 877 if cit.RecommendedSettings.Key.Type != "" { 878 defaultKP.KeyType = &cit.RecommendedSettings.Key.Type 879 shouldCreateDefaultKeyPAir = true 880 } 881 882 if cit.RecommendedSettings.Key.Length > 0 { 883 defaultKP.RsaKeySize = &cit.RecommendedSettings.Key.Length 884 shouldCreateDefaultKeyPAir = true 885 } 886 887 if cit.RecommendedSettings.Key.Curve != "" { 888 defaultKP.EllipticCurve = &cit.RecommendedSettings.Key.Curve 889 shouldCreateDefaultKeyPAir = true 890 } 891 892 if shouldCreateDefaultKeyPAir { 893 if ps.Default == nil { 894 ps.Default = &policy.Default{} 895 } 896 ps.Default.KeyPair = &defaultKP 897 } 898 899 return &ps 900 } 901 902 func contains(values []string, toSearch string) bool { 903 copiedValues := make([]string, len(values)) 904 copy(copiedValues, values) 905 sort.Strings(copiedValues) 906 907 return binarySearch(copiedValues, toSearch) >= 0 908 } 909 910 func binarySearch(values []string, toSearch string) int { 911 length := len(values) - 1 912 minimum := 0 913 for minimum <= length { 914 mid := length - (length-minimum)/2 915 if strings.Compare(toSearch, values[mid]) > 0 { 916 minimum = mid + 1 917 } else if strings.Compare(toSearch, values[mid]) < 0 { 918 length = mid - 1 919 } else { 920 return mid 921 } 922 } 923 return -1 924 } 925 926 func parseCitResult(expectedStatusCode int, httpStatusCode int, httpStatus string, body []byte) (*certificateTemplate, error) { 927 if httpStatusCode == expectedStatusCode { 928 return parseCitDetailsData(body, httpStatusCode) 929 } 930 respErrors, err := parseResponseErrors(body) 931 if err != nil { 932 return nil, err // parseResponseErrors always return verror.ServerError 933 } 934 respError := fmt.Sprintf("unexpected status code on Venafi Cloud registration. Status: %s\n", httpStatus) 935 for _, e := range respErrors { 936 respError += fmt.Sprintf("Error Code: %d Error: %s\n", e.Code, e.Message) 937 } 938 return nil, fmt.Errorf("%w: %v", verror.ServerError, respError) 939 } 940 941 func parseCitDetailsData(b []byte, status int) (*certificateTemplate, error) { 942 943 var cits CertificateTemplates 944 var cit certificateTemplate 945 946 if status == http.StatusOK { //update case 947 err := json.Unmarshal(b, &cit) 948 949 if err != nil { 950 return nil, err 951 } 952 } else { //create case 953 err := json.Unmarshal(b, &cits) 954 955 if err != nil { 956 return nil, err 957 } 958 959 //we just get the cit we created/updated 960 cit = cits.CertificateTemplates[0] 961 } 962 963 return &cit, nil 964 } 965 966 func isWildCard(cnRegex []string) bool { 967 if len(cnRegex) > 0 { 968 for _, val := range cnRegex { 969 if !(strings.HasPrefix(val, "[*a")) { 970 return false 971 } 972 } 973 return true 974 } 975 return false 976 } 977 978 func getServiceAccountTokenURL(rawURL string) (string, error) { 979 // removing trailing slash from util.NormalizeURL function 980 _, err := url.ParseRequestURI(rawURL) 981 if err != nil { 982 return "", fmt.Errorf("token url error: %w", err) 983 } 984 985 return rawURL, nil 986 }