package server import ( "crypto/rand" "crypto/rsa" "crypto/x509" "encoding/base64" "encoding/pem" "errors" "fmt" "html/template" "math/big" "strings" "github.com/fleetdm/fleet/v4/server/bindata" platformhttp "github.com/fleetdm/fleet/v4/server/platform/http" ) // GenerateRandomEmail generates a random email using baseEmail as the base. // For example: GenerateRandomEmail('email@example.com') -> 'email+somerandomtext@example.com' func GenerateRandomEmail(baseEmail string) (string, error) { emailSuffix, err := GenerateRandomURLSafeText(10) if err != nil { return "", err } atIdx := strings.Index(baseEmail, "@") var email string if atIdx < 0 { email = fmt.Sprintf("%s+%s", baseEmail, emailSuffix) } else { email = fmt.Sprintf("%s+%s%s", baseEmail[:atIdx], emailSuffix, baseEmail[atIdx:]) } return email, nil } // GenerateRandomPwd generates a random text that // complies with Fleet's password requirements. func GenerateRandomPwd() (string, error) { pwd, err := GenerateRandomText(14) if err != nil { return "", err } n, err := rand.Int(rand.Reader, big.NewInt(int64(100))) if err != nil { return "", err } return fmt.Sprintf("%s%d!", pwd, n.Int64()), nil } // GenerateRandomText return a string generated by filling in keySize bytes with // random data and then base64 encoding those bytes func GenerateRandomText(keySize int) (string, error) { key := make([]byte, keySize) _, err := rand.Read(key) if err != nil { return "", err } return base64.StdEncoding.EncodeToString(key), nil } // GenerateRandomURLSafeText return a string generated by filling in keySize bytes with // random data and then URL-safe base64 encoding those bytes func GenerateRandomURLSafeText(keySize int) (string, error) { key := make([]byte, keySize) _, err := rand.Read(key) if err != nil { return "", err } return base64.URLEncoding.EncodeToString(key), nil } // PostJSONWithTimeout marshals v as JSON and POSTs it to the given URL with a 30-second timeout. // // Deprecated: Use github.com/fleetdm/fleet/v4/server/platform/http.PostJSONWithTimeout instead. var PostJSONWithTimeout = platformhttp.PostJSONWithTimeout // MaskSecretURLParams masks URL query values if the query param name includes "secret", "token", // "key", "password". It accepts a raw string and returns a redacted string if the raw string is // URL-parseable. If it is not URL-parseable, the raw string is returned unchanged. // // Deprecated: Use github.com/fleetdm/fleet/v4/server/platform/http.MaskSecretURLParams instead. var MaskSecretURLParams = platformhttp.MaskSecretURLParams // MaskURLError checks if the provided error is a *url.Error. If so, it applies MaskSecretURLParams // to the URL value and returns the modified error. If not, the error is returned unchanged. // // Deprecated: Use github.com/fleetdm/fleet/v4/server/platform/http.MaskURLError instead. var MaskURLError = platformhttp.MaskURLError // TODO: Consider moving other crypto functions from server/mdm/apple/util to here // DecodePrivateKeyPEM decodes PEM-encoded private key data. func DecodePrivateKeyPEM(encoded []byte) (*rsa.PrivateKey, error) { block, _ := pem.Decode(encoded) if block == nil { return nil, errors.New("no PEM-encoded data found") } if block.Type != "RSA PRIVATE KEY" { return nil, fmt.Errorf("unexpected block type %s", block.Type) } return x509.ParsePKCS1PrivateKey(block.Bytes) } // GetTemplate takes a path to a template file and a template name and will // include the template file in the build binary. It then returns a pointer to // the template. func GetTemplate(templatePath string, templateName string) (*template.Template, error) { templateData, err := bindata.Asset(templatePath) if err != nil { return nil, err } t, err := template.New(templateName).Parse(string(templateData)) if err != nil { return nil, err } return t, nil } // Base64DecodePaddingAgnostic decodes a base64 string that might be encoded // using raw encoding or standard encoding (padded) func Base64DecodePaddingAgnostic(s string) ([]byte, error) { us := strings.TrimRight(s, string(base64.StdPadding)) return base64.RawStdEncoding.DecodeString(us) } // RemoveDuplicatesFromSlice returns a slice with all the duplicates removed from the input slice. func RemoveDuplicatesFromSlice[T comparable](slice []T) []T { // We are using the allKeys map as a set here allKeys := make(map[T]struct{}, len(slice)) list := make([]T, 0, len(slice)) for _, i := range slice { if _, exists := allKeys[i]; !exists { allKeys[i] = struct{}{} list = append(list, i) } } return list } // NeverTimestamp is the timestamp used to indicate that an event has never // occurred. For example, `hosts.detail_updated_at` is set to this value to indicate // that Fleet has never updated the host vitals. const NeverTimestamp = "2000-01-01 00:00:00"