go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/network/resources/dns.go (about)

     1  // Copyright (c) Mondoo, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  package resources
     5  
     6  import (
     7  	"crypto/sha256"
     8  	"encoding/hex"
     9  	"errors"
    10  	"strconv"
    11  	"strings"
    12  
    13  	"github.com/miekg/dns"
    14  	"go.mondoo.com/cnquery/llx"
    15  	"go.mondoo.com/cnquery/providers-sdk/v1/plugin"
    16  	"go.mondoo.com/cnquery/providers-sdk/v1/util/convert"
    17  	"go.mondoo.com/cnquery/providers/network/connection"
    18  	"go.mondoo.com/cnquery/providers/network/resources/dnsshake"
    19  	"go.mondoo.com/cnquery/providers/network/resources/domain"
    20  	"go.mondoo.com/cnquery/types"
    21  	"go.mondoo.com/cnquery/utils/sortx"
    22  )
    23  
    24  func (d *mqlDomainName) id() (string, error) {
    25  	return "domainName/" + d.Fqdn.Data, nil
    26  }
    27  
    28  func initDomainName(runtime *plugin.Runtime, args map[string]*llx.RawData) (map[string]*llx.RawData, plugin.Resource, error) {
    29  	fqdn, ok := args["fqdn"]
    30  	if !ok {
    31  		conn := runtime.Connection.(*connection.HostConnection)
    32  		fqdn = llx.StringData(conn.FQDN())
    33  		args["fqdn"] = fqdn
    34  	}
    35  
    36  	if fqdn == nil {
    37  		return nil, nil, errors.New("domainName resource requires fqdn argument")
    38  	}
    39  
    40  	dn, err := domain.Parse(fqdn.Value.(string))
    41  	if err != nil {
    42  		return nil, nil, err
    43  	}
    44  
    45  	args["effectiveTLDPlusOne"] = llx.StringData(dn.EffectiveTLDPlusOne)
    46  	args["tld"] = llx.StringData(dn.TLD)
    47  	args["tldIcannManaged"] = llx.BoolData(dn.IcannManagedTLD)
    48  	args["labels"] = llx.ArrayData(llx.TArr2Raw[string](dn.Labels), types.String)
    49  
    50  	return args, nil, nil
    51  }
    52  
    53  func (d *mqlDns) id() (string, error) {
    54  	return "dns/" + d.Fqdn.Data, nil
    55  }
    56  
    57  func initDns(runtime *plugin.Runtime, args map[string]*llx.RawData) (map[string]*llx.RawData, plugin.Resource, error) {
    58  	_, ok := args["fqdn"]
    59  	if !ok {
    60  		conn := runtime.Connection.(*connection.HostConnection)
    61  		args["fqdn"] = llx.StringData(conn.FQDN())
    62  	}
    63  
    64  	return args, nil, nil
    65  }
    66  
    67  func (d *mqlDns) params(fqdn string) (interface{}, error) {
    68  	dnsShaker, err := dnsshake.New(fqdn)
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  
    73  	records, err := dnsShaker.Query()
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  
    78  	return convert.JsonToDict(records)
    79  }
    80  
    81  func (d *mqlDns) records(params interface{}) ([]interface{}, error) {
    82  	// NOTE: mql does not cache the results of GetRecords since it has an input argument
    83  	// Iterations over map keys are not deterministic and therefore we need to sort the keys
    84  
    85  	paramsM, ok := params.(map[string]interface{})
    86  	if !ok {
    87  		return nil, errors.New("incorrect structure of params received")
    88  	}
    89  
    90  	// convert responses to dns types
    91  	resultMap := make(map[string]*mqlDnsRecord)
    92  	for k := range paramsM {
    93  		r, ok := paramsM[k].(map[string]interface{})
    94  		if !ok {
    95  			return nil, errors.New("incorrect structure of params entries received")
    96  		}
    97  
    98  		// filter by successful dns records
    99  		if r["rCode"] != dns.RcodeToString[dns.RcodeSuccess] {
   100  			continue
   101  		}
   102  
   103  		var ttl *llx.RawData
   104  		if r["TTL"] == nil {
   105  			ttl = llx.NilData
   106  		} else {
   107  			ttl = llx.IntData(r["TTL"].(int64))
   108  		}
   109  		o, err := CreateResource(d.MqlRuntime, "dns.record", map[string]*llx.RawData{
   110  			"name":  llx.StringData(r["name"].(string)),
   111  			"ttl":   ttl,
   112  			"class": llx.StringData(r["class"].(string)),
   113  			"type":  llx.StringData(r["type"].(string)),
   114  			"rdata": llx.ArrayData(llx.TArr2Raw(r["rData"].([]interface{})), types.String),
   115  		})
   116  		if err != nil {
   117  			return nil, err
   118  		}
   119  
   120  		record := o.(*mqlDnsRecord)
   121  		resultMap[record.__id] = record
   122  	}
   123  
   124  	keys := sortx.Keys(resultMap)
   125  	res := []interface{}{}
   126  	for i := range keys {
   127  		res = append(res, resultMap[keys[i]])
   128  	}
   129  
   130  	return res, nil
   131  }
   132  
   133  func (d *mqlDnsRecord) id() (string, error) {
   134  	return "dns.record/" + d.Name.Data + "/" + d.Class.Data + "/" + d.Type.Data, nil
   135  }
   136  
   137  func (d *mqlDns) mx(params interface{}) ([]interface{}, error) {
   138  	paramsM, ok := params.(map[string]interface{})
   139  	if !ok {
   140  		return []interface{}{}, nil
   141  	}
   142  
   143  	mxEntries := []interface{}{}
   144  	record, ok := paramsM["MX"]
   145  	if !ok {
   146  		return mxEntries, nil
   147  	}
   148  
   149  	r := record.(map[string]interface{})
   150  
   151  	var name, c, t string
   152  	var ttl int64
   153  	var rdata []interface{}
   154  
   155  	if r["name"] != nil {
   156  		name = r["name"].(string)
   157  	}
   158  
   159  	if r["class"] != nil {
   160  		c = r["class"].(string)
   161  	}
   162  
   163  	if r["type"] != nil {
   164  		t = r["type"].(string)
   165  	}
   166  
   167  	if r["TTL"] != nil {
   168  		ttl = r["TTL"].(int64)
   169  	}
   170  
   171  	if r["rData"] != nil {
   172  		rdata = r["rData"].([]interface{})
   173  	}
   174  
   175  	for j := range rdata {
   176  		entry := rdata[j].(string)
   177  
   178  		// use dns package to parse mx entry
   179  		s := name + "\t" + strconv.FormatInt(ttl, 10) + "\t" + c + "\t" + t + "\t" + entry
   180  		got, err := dns.NewRR(s)
   181  		if err != nil {
   182  			return nil, err
   183  		}
   184  
   185  		switch v := got.(type) {
   186  		case *dns.MX:
   187  			mxEntry, err := CreateResource(d.MqlRuntime, "dns.mxRecord", map[string]*llx.RawData{
   188  				"name":       llx.StringData(name),
   189  				"preference": llx.IntData(int64(v.Preference)),
   190  				"domainName": llx.StringData(v.Mx),
   191  			})
   192  			if err != nil {
   193  				return nil, err
   194  			}
   195  			mxEntries = append(mxEntries, mxEntry)
   196  		}
   197  	}
   198  
   199  	return mxEntries, nil
   200  }
   201  
   202  func (d *mqlDnsMxRecord) id() (string, error) {
   203  	return "dns.mx/" + d.Name.Data + "+" + d.DomainName.Data, nil
   204  }
   205  
   206  func (d *mqlDns) dkim(params interface{}) ([]interface{}, error) {
   207  	paramsM, ok := params.(map[string]interface{})
   208  	if !ok {
   209  		return []interface{}{}, nil
   210  	}
   211  
   212  	dkimEntries := []interface{}{}
   213  
   214  	record, ok := paramsM["TXT"]
   215  	if !ok {
   216  		return dkimEntries, nil
   217  	}
   218  
   219  	r := record.(map[string]interface{})
   220  
   221  	var name string
   222  	var rdata []interface{}
   223  
   224  	if r["name"] != nil {
   225  		name = r["name"].(string)
   226  	}
   227  
   228  	if r["rData"] != nil {
   229  		rdata = r["rData"].([]interface{})
   230  	}
   231  
   232  	for j := range rdata {
   233  		entry := rdata[j].(string)
   234  		entry = strings.TrimSpace(entry)
   235  
   236  		if !strings.HasPrefix(entry, "v=DKIM1;") {
   237  			continue
   238  		}
   239  
   240  		dkimRepr, err := dnsshake.NewDkimPublicKeyRepresentation(entry)
   241  		if err != nil {
   242  			return nil, err
   243  		}
   244  
   245  		o, err := CreateResource(d.MqlRuntime, "dns.dkimRecord", map[string]*llx.RawData{
   246  			"domain":         llx.StringData(name),
   247  			"dnsTxt":         llx.StringData(entry),
   248  			"version":        llx.StringData(dkimRepr.Version),
   249  			"hashAlgorithms": llx.ArrayData(llx.TArr2Raw(dkimRepr.HashAlgorithms), types.String),
   250  			"keyType":        llx.StringData(dkimRepr.KeyType),
   251  			"notes":          llx.StringData(dkimRepr.Notes),
   252  			"publicKeyData":  llx.StringData(dkimRepr.PublicKeyData),
   253  			"serviceTypes":   llx.ArrayData(llx.TArr2Raw(dkimRepr.ServiceType), types.String),
   254  			"flags":          llx.ArrayData(llx.TArr2Raw(dkimRepr.Flags), types.String),
   255  		})
   256  		if err != nil {
   257  			return nil, err
   258  		}
   259  		record := o.(*mqlDnsDkimRecord)
   260  		record.dkim = dkimRepr
   261  		dkimEntries = append(dkimEntries, record)
   262  	}
   263  
   264  	return dkimEntries, nil
   265  }
   266  
   267  type mqlDnsDkimRecordInternal struct {
   268  	dkim *dnsshake.DkimPublicKeyRepresentation
   269  }
   270  
   271  func (d *mqlDnsDkimRecord) id() (string, error) {
   272  	hasher := sha256.New()
   273  	hasher.Write([]byte(d.DnsTxt.Data))
   274  	sha256 := hex.EncodeToString(hasher.Sum(nil))
   275  	return "dns.dkim/" + d.Domain.Data + "/" + sha256, nil
   276  }
   277  
   278  func (d *mqlDnsDkimRecord) valid() (bool, error) {
   279  	if d.dkim == nil {
   280  		return false, errors.New("could not load dkim data")
   281  	}
   282  
   283  	ok, _, _ := d.dkim.Valid()
   284  	return ok, nil
   285  }