Re: IOMedia filter scheme and CD/DVD media
Re: IOMedia filter scheme and CD/DVD media
- Subject: Re: IOMedia filter scheme and CD/DVD media
- From: Guy Helmer <email@hidden>
- Date: Thu, 04 Apr 2013 13:16:06 -0500
On Feb 5, 2013, at 12:55 PM, Kevin Elliott <email@hidden> wrote:
>
> On Feb 5, 2013, at 8:34 AM, Phil Jordan <email@hidden> wrote:
>
>> Hi,
>>
>> On Tue, Feb 5, 2013 at 12:41 AM, Guy Helmer <email@hidden> wrote:
>>> I'm looking into a way to thoroughly disable writing to CD or DVD media on OS X to support data security requirements, and it seems like an IOMedia filter scheme that simply manipulates the media's isWritable value would do the trick.
>>>
>>> However, in reading the Mass Storage Device Driver Programming Guide's section Developing a Filter Scheme, I've found this warning:
>>>
>>> "a filter-scheme driver should not produce an IOCDMedia or IODVDMedia object, because these objects have provider requirements specific to CD and DVD media that can be met only by an IOCDBlockStorageDriver or IODVDBlockStorageDriver, respectively."
>>
>> If you look at the source for IOCDMedia, it does indeed cast its
>> provider IOService to IOCDBlockStorageDriver without checking, and
>> then calls IOCDBlockStorageDriver methods. So this warning is
>> definitely to be heeded.
>>
>>> As a newbie in IOKit, there is probably something obvious that I am missing… For my purposes, does this mean that I am unable to create a filter that could sit in the CD/DVD storage driver stack, even if it just uses the IOMedia interface? If so, are there any likely alternatives I should pursue?
>>
>> IOKit is maybe not the ideal place to do security-related stuff as Ben
>> Gollmer has already suggested. I guess it depends on what you're
>> trying to do; I've never tried it, but you could probably implement a
>> forwarding "filter" IOCDBlockStorageDriver that sits on top of the
>> "real" IOCDMedia. Another disadvantage of a filter scheme for security
>> purposes is that both the original and the new device nodes are
>> created in /dev/ (via the IOMediaBSDClient). The "real" node should
>> deny access with exclusive access errors, but that might not apply to
>> all operations.
>
> Nothing at the IOMedia layer will work for this, though it might look like it will. Our CD burning APIs would probably honor this sort of change, but all it takes to actually burn a CD is the SCSITaskUserClient which connects well below the media layer.
>
> If you're running on a controlled set of hardware, you could probably make a driver that convinced the SCSI stack that it was looking at a standard CD/DVD drive and not a burner but I'm not sure if even that would solve your problem (I don't remember if non-burners can be accessed via the SCSITaskUserClient).
>
> -Kevin
Thanks for everyone's advice. So far, I have not been able to see any ioctl()'s being used during CD/DVD operations -- I built a module to implement a MAC policy to monitor all ioctl() calls, but none appeared for the vnode device /dev/diskN or /dev/diskNs1 (FWIW, hooking into the MAC subsystem was painful anyway because the mac_policy_register() and mac_policy_unregister() symbols are not exported for linking with 10.8's kernel).
It looks to me as though CD/DVD burning must be occurring through the SCSITaskUserClient. I've tried subclassing IOSCSIPeripheralDeviceNub to check responses from mode sense mechanical capabilities requests, but on one of my test machines (under VMware Fusion) the IOSCSIPeripheralDeviceNub doesn't show up in the IOKit chain between AppleLSIFusionSCSI and IOSCSIPeripheralDeviceType05:
| | | | +-o AppleLSIFusionSCSI <class AppleLSIFusionSCSI, id 0x10000023a, registered, matched, active, busy 0 (17 ms), retain 36>
| | | | | {
| | | | | "IOClass" = "AppleLSIFusionSCSI"
| | | | | "Write Time Out Duration" = 30000
| | | | | "IOMaximumSegmentByteCountRead" = 16777200
| | | | | "IOMaximumSegmentByteCountWrite" = 16777200
| | | | | "SCSI Initiator Identifier" = 7
| | | | | "IOProviderClass" = "IOPCIDevice"
| | | | | "Physical Interconnect Location" = "Internal"
| | | | | "IOMaximumSegmentAddressableBitCount" = 64
| | | | | "IOMaximumSegmentCountRead" = 256
| | | | | "IOCommandPoolSize" = 128
| | | | | "IOProbeScore" = 0
| | | | | "IOMaximumSegmentCountWrite" = 256
| | | | | "Read Time Out Duration" = 30000
| | | | | "IONameMatch" = ("LSILogic,scsi","pci1000,30")
| | | | | "Physical Interconnect" = "SCSI Parallel Interface"
| | | | | "SCSI Hierarchical Logical Unit Support" = Yes
| | | | | "Controller Characteristics" = {"Port Description"="Channel A","Bus Width"="Wide","SCSI Parallel Signaling Type"="Low Voltage Differential","Product Name"="Ultra320 SCSI HBA","Vendor Name"="LSILogic","Product Revision Level"=""}
| | | | | "IOMatchCategory" = "IODefaultMatchCategory"
| | | | | "CFBundleIdentifier" = "com.apple.driver.AppleLSIFusionMPT"
| | | | | "IOMinimumSegmentAlignmentByteCount" = 4
| | | | | "IONameMatched" = "pci1000,30"
| | | | | "Protocol Characteristics" = {"Physical Interconnect"="SCSI Parallel Interface","Read Time Out Duration"=30000,"SCSI Domain Identifier"=1,"Physical Interconnect Location"="Internal","Write Time Out Duration"=30000}
| | | | | "Manages Targets" = No
| | | | | "VMw" = "External"
| | | | | "IOUnitName" = "disk"
| | | | | }
| | | | |
| | | | +-o IOSCSIParallelInterfaceDevice@0 <class IOSCSIParallelInterfaceDevice, id 0x100000250, registered, matched, active, busy 0 (17 ms), retain 10>
| | | | | | {
| | | | | | "IOUnit" = 0
| | | | | | "IOPowerManagement" = {"ChildrenPowerState"=1,"MaxPowerState"=1,"DevicePowerState"=1,"CurrentPowerState"=1,"ChildProxyPowerState"=1,"DriverPowerState"=1}
| | | | | | "Protocol Characteristics" = {"Write Time Out Duration"=30000,"SCSI Device Features"=3,"Read Time Out Duration"=30000,"Physical Interconnect"="SCSI Parallel Interface","SCSI Target Identifier"=0,"Physical Interconnect Location"="Internal","SCSI I_T Nexus Features"=3,"SCSI Domain Identifier"=1}
| | | | | | }
| | | | | |
| | | | | +-o IOSCSITargetDevice <class IOSCSITargetDevice, id 0x100000252, registered, matched, active, busy 0 (17 ms), retain 10>
| | | | | | {
| | | | | | "Product Revision Level" = "1.0"
| | | | | | "Peripheral Device Type" = 5
| | | | | | "Vendor Identification" = "VMware,"
| | | | | | "Product Identification" = "Virtual CD-ROM"
| | | | | | "Protocol Characteristics" = {"Physical Interconnect"="SCSI Parallel Interface","Read Time Out Duration"=30000,"SCSI Target Identifier"=0,"SCSI Domain Identifier"=1,"Physical Interconnect Location"="Internal","Write Time Out Duration"=30000}
| | | | | | "IOPowerManagement" = {"TimeSinceDeviceIdle"=210881,"MaxPowerState"=1,"ChildProxyPowerState"=1,"ActivityTickles"=0,"DevicePowerState"=1,"IdleTimerPeriod"=600000,"ChildrenPowerState"=1,"DriverPowerState"=1,"CurrentPowerState"=1}
| | | | | | "Hierarchical LUN Support" = No
| | | | | | }
| | | | | |
| | | | | +-o IOSCSIHierarchicalLogicalUnit@0000000000000000 <class IOSCSIHierarchicalLogicalUnit, id 0x100000256, registered, matched, active, busy 0 (17 ms), retain 10>
| | | | | | {
| | | | | | "SCSI Logical Unit Bytes" = <0000000000000000>
| | | | | | "IOPowerManagement" = {"ChildrenPowerState"=1,"MaxPowerState"=1,"DevicePowerState"=1,"CurrentPowerState"=1,"ChildProxyPowerState"=1,"DriverPowerState"=1}
| | | | | | "CurrentPowerState" = 1
| | | | | | "Peripheral Device Type" = 5
| | | | | | "SCSI Logical Unit Number" = 0
| | | | | | "IOMatchCategory" = "SCSITaskUserClientIniter"
| | | | | | "Vendor Identification" = "VMware,"
| | | | | | "Protocol Characteristics" = {"Write Time Out Duration"=30000,"Read Time Out Duration"=30000,"Physical Interconnect"="SCSI Parallel Interface","SCSI Target Identifier"=0,"Physical Interconnect Location"="Internal","SCSI Logical Unit Number"=0,"SCSI Logical Unit Bytes"=<0000000000000000>,"SCSI Domain Identifier"=1}
| | | | | | "Product Revision Level" = "1.0"
| | | | | | "Product Identification" = "Virtual CD-ROM"
| | | | | | "IOUnitLUN" = 0
| | | | | | "TPGS Information" = 0
| | | | | | "IOUnitLUNBytes" = <0000000000000000>
| | | | | | }
| | | | | |
| | | | | +-o IOSCSIPeripheralDeviceType05 <class IOSCSIPeripheralDeviceType05, id 0x10000025b, !registered, !matched, active, busy 0 (0 ms), retain 9>
| | | | | | {
| | | | | | "IOClass" = "IOSCSIPeripheralDeviceType05"
| | | | | | "CFBundleIdentifier" = "com.apple.iokit.IOSCSIMultimediaCommandsDevice"
| | | | | | "IOProviderClass" = "IOSCSIPeripheralDeviceNub"
| | | | | | "IOMaximumBlockCountRead" = 65535
| | | | | | "IOPowerManagement" = {"TimeSinceDeviceIdle"=10496,"MaxPowerState"=4,"ActivityTickles"=0,"IdleTimerPeriod"=200000,"DevicePowerState"=3,"DriverPowerState"=1,"CurrentPowerState"=3}
| | | | | | "IOMaximumBlockCountWrite" = 65535
| | | | | | "CD Features" = 1599
| | | | | | "IOProbeScore" = 5000
| | | | | | "Peripheral Device Type" = 5
| | | | | | "BD Features" = 0
| | | | | | "IOMatchCategory" = "IODefaultMatchCategory"
| | | | | | "DVD Features" = 111
| | | | | | }
| | | | | |
| | | | | +-o IODVDServices <class IODVDServices, id 0x100000287, registered, matched, active, busy 0 (0 ms), retain 7>
| | | | | | {
| | | | | | "IOMatchCategory" = "SCSITaskUserClientIniter"
| | | | | | "SCSITaskUserClient GUID" = <0022a50680ffffff0c7afe1900000000>
| | | | | | "device-type" = "DVD"
| | | | | | "IOCFPlugInTypes" = {"97ABCF2C-23CC-11D5-A0E8-003065704866"="IOSCSIArchitectureModelFamily.kext/Contents/PlugIns/SCSITaskUserClient.kext/Contents/PlugIns/SCSITaskLib.plugin"}
| | | | | | "IOUserClientClass" = "SCSITaskUserClient"
| | | | | | "Protocol Characteristics" = {"Write Time Out Duration"=30000,"Read Time Out Duration"=30000,"Physical Interconnect"="SCSI Parallel Interface","SCSI Target Identifier"=0,"Physical Interconnect Location"="Internal","SCSI Logical Unit Number"=0,"SCSI Logical Unit Bytes"=<0000000000000000>,"SCSI Domain Identifier"=1}
| | | | | | "Device Characteristics" = {"Power Off"=No,"Product Name"="Virtual CD-ROM","Fast Spindown"=No,"CD Features"=1599,"Low Power Polling"=No,"DVD Features"=111,"BD Features"=0,"Vendor Name"="VMware,","Async Notification"=No,"Loading Mechanism"="Tray","Product Revision Level"="1.0"}
| | | | | | "SCSITaskDeviceCategory" = "SCSITaskAuthoringDevice"
| | | | | | }
| | | | | |
| | | | | +-o SCSITaskUserClientIniter <class SCSITaskUserClientIniter, id 0x100000288, !registered, !matched, active, busy 0, retain 4>
| | | | | | {
| | | | | | "IOClass" = "SCSITaskUserClientIniter"
| | | | | | "IOProviderClass" = "IODVDServices"
| | | | | | "IOMatchCategory" = "SCSITaskUserClientIniter"
| | | | | | "IOProviderMergeProperties" = {"IOCFPlugInTypes"={"97ABCF2C-23CC-11D5-A0E8-003065704866"="IOSCSIArchitectureModelFamily.kext/Contents/PlugIns/SCSITaskUserClient.kext/Contents/PlugIns/SCSITaskLib.plugin"},"IOUserClientClass"="SCSITaskUserClient","SCSITaskDeviceCategory"="SCSITaskAuthoringDevice"}
| | | | | | "CFBundleIdentifier" = "com.apple.iokit.SCSITaskUserClient"
| | | | | | "IOProbeScore" = 0
| | | | | | }
| | | | | |
| | | | | +-o IODVDBlockStorageDriver <class IODVDBlockStorageDriver, id 0x100000289, registered, matched, active, busy 0 (0 ms), retain 6>
| | | | | {
| | | | | "IOPropertyMatch" = {"device-type"="DVD"}
| | | | | "IOProbeScore" = 0
| | | | | "IOProviderClass" = "IODVDBlockStorageDevice"
| | | | | "IOClass" = "IODVDBlockStorageDriver"
| | | | | "CFBundleIdentifier" = "com.apple.iokit.IODVDStorageFamily"
| | | | | "Statistics" = {"Operations (Write)"=0,"Latency Time (Write)"=0,"Bytes (Read)"=0,"Errors (Write)"=0,"Total Time (Read)"=0,"Retries (Read)"=0,"Latency Time (Read)"=0,"Errors (Read)"=0,"Total Time (Write)"=0,"Bytes (Write)"=0,"Operations (Read)"=0,"Retries (Write)"=0}
| | | | | "IOMatchCategory" = "IODefaultMatchCategory"
| | | | | }
-------------------------------------------------------------------------------------
So, I've subclassed the IOSCSIMultimediaCommandsDevice and its subclass IOSCSIPeripheralDeviceType05, and am seeing this chain when the kernel boots with my kext installed:
| | | | +-o AppleLSIFusionSCSI <class AppleLSIFusionSCSI, id 0x10000023a, registered,
matched, active, busy 0 (7 ms), retain 36>
| | | | | {
| | | | | "IOClass" = "AppleLSIFusionSCSI"
| | | | | "Write Time Out Duration" = 30000
| | | | | "IOMaximumSegmentByteCountRead" = 16777200
| | | | | "IOMaximumSegmentByteCountWrite" = 16777200
| | | | | "SCSI Initiator Identifier" = 7
| | | | | "IOProviderClass" = "IOPCIDevice"
| | | | | "Physical Interconnect Location" = "Internal"
| | | | | "IOMaximumSegmentAddressableBitCount" = 64
| | | | | "IOMaximumSegmentCountRead" = 256
| | | | | "IOCommandPoolSize" = 128
| | | | | "IOProbeScore" = 0
| | | | | "IOMaximumSegmentCountWrite" = 256
| | | | | "Read Time Out Duration" = 30000
| | | | | "IONameMatch" = ("LSILogic,scsi","pci1000,30")
| | | | | "Physical Interconnect" = "SCSI Parallel Interface"
| | | | | "SCSI Hierarchical Logical Unit Support" = Yes
| | | | | "Controller Characteristics" = {"Port Description"="Channel A","Bus Width"="Wide","SCSI Parallel Signaling Type"="Low Voltage Differential","Product Name"="Ultra320 SCSI HBA","Vendor Name"="LSILogic","Product Revision Level"=""}
| | | | | "IOMatchCategory" = "IODefaultMatchCategory"
| | | | | "CFBundleIdentifier" = "com.apple.driver.AppleLSIFusionMPT"
| | | | | "IOMinimumSegmentAlignmentByteCount" = 4
| | | | | "IONameMatched" = "pci1000,30"
| | | | | "Protocol Characteristics" = {"Physical Interconnect"="SCSI Parallel Interface","Read Time Out Duration"=30000,"SCSI Domain Identifier"=1,"Physical Interconnect Location"="Internal","Write Time Out Duration"=30000}
| | | | | "Manages Targets" = No
| | | | | "VMw" = "External"
| | | | | "IOUnitName" = "disk"
| | | | | }
| | | | |
| | | | +-o IOSCSIParallelInterfaceDevice@0 <class IOSCSIParallelInterfaceDevice, id 0x100000250, registered, matched, active, busy 0 (7 ms), retain 10>
| | | | | | {
| | | | | | "IOUnit" = 0
| | | | | | "IOPowerManagement" = {"ChildrenPowerState"=1,"MaxPowerState"=1,"DevicePowerState"=1,"CurrentPowerState"=1,"ChildProxyPowerState"=1,"DriverPowerState"=1}
| | | | | | "Protocol Characteristics" = {"Write Time Out Duration"=30000,"SCSI Device Features"=3,"Read Time Out Duration"=30000,"Physical Interconnect"="SCSI Parallel Interface","SCSI Target Identifier"=0,"Physical Interconnect Location"="Internal","SCSI I_T Nexus Features"=3,"SCSI Domain Identifier"=1}
| | | | | | }
| | | | | |
| | | | | +-o IOSCSITargetDevice <class IOSCSITargetDevice, id 0x100000252, registered, matched, active, busy 0 (7 ms), retain 9>
| | | | | | {
| | | | | | "Product Revision Level" = "1.0"
| | | | | | "Peripheral Device Type" = 5
| | | | | | "Vendor Identification" = "VMware,"
| | | | | | "Product Identification" = "Virtual CD-ROM"
| | | | | | "Protocol Characteristics" = {"Physical Interconnect"="SCSI Parallel Interface","Read Time Out Duration"=30000,"SCSI Target Identifier"=0,"SCSI Domain Identifier"=1,"Physical Interconnect Location"="Internal","Write Time Out Duration"=30000}
| | | | | | "IOPowerManagement" = {"TimeSinceDeviceIdle"=2135710,"MaxPowerState"=1,"ChildProxyPowerState"=1,"ActivityTickles"=0,"DevicePowerState"=0,"IdleTimerPeriod"=600000,"ChildrenPowerState"=1,"DriverPowerState"=1,"CurrentPowerState"=1}
| | | | | | "Hierarchical LUN Support" = No
| | | | | | }
| | | | | |
| | | | | +-o IOSCSIHierarchicalLogicalUnit@0000000000000000 <class IOSCSIHierarchicalLogicalUnit, id 0x100000256, registered, matched, active, busy 0 (7 ms), retain 10>
| | | | | | {
| | | | | | "SCSI Logical Unit Bytes" = <0000000000000000>
| | | | | | "IOPowerManagement" = {"ChildrenPowerState"=1,"MaxPowerState"=1,"DevicePowerState"=1,"CurrentPowerState"=1,"ChildProxyPowerState"=1,"DriverPowerState"=1}
| | | | | | "CurrentPowerState" = 1
| | | | | | "Peripheral Device Type" = 5
| | | | | | "SCSI Logical Unit Number" = 0
| | | | | | "IOMatchCategory" = "SCSITaskUserClientIniter"
| | | | | | "Vendor Identification" = "VMware,"
| | | | | | "Protocol Characteristics" = {"Write Time Out Duration"=30000,"Read Time Out Duration"=30000,"Physical Interconnect"="SCSI Parallel Interface","SCSI Target Identifier"=0,"Physical Interconnect Location"="Internal","SCSI Logical Unit Number"=0,"SCSI Logical Unit Bytes"=<0000000000000000>,"SCSI Domain Identifier"=1}
| | | | | | "Product Revision Level" = "1.0"
| | | | | | "Product Identification" = "Virtual CD-ROM"
| | | | | | "IOUnitLUN" = 0
| | | | | | "TPGS Information" = 0
| | | | | | "IOUnitLUNBytes" = <0000000000000000>
| | | | | | }
| | | | | |
| | | | | +-o IOSCSIPeripheralDeviceType05Filter <class IOSCSIPeripheralDeviceType05Filter, id 0x10000025b, !registered, !matched, active, busy 0, retain 7>
| | | | | | {
| | | | | | "IOClass" = "IOSCSIPeripheralDeviceType05Filter"
| | | | | | "CFBundleIdentifier" = "com.work.IOSCSIMultimediaCommandsDeviceFilter"
| | | | | | "IOProviderClass" = "IOSCSIPeripheralDeviceNub"
| | | | | | "IOMaximumBlockCountRead" = 65535
| | | | | | "IOPowerManagement" = {"TimeSinceDeviceIdle"=1568706,"MaxPowerState"=4,"ActivityTickles"=0,"IdleTimerPeriod"=200000,"DevicePowerState"=0,"DriverPowerState"=1,"CurrentPowerState"=1}
| | | | | | "IOMaximumBlockCountWrite" = 65535
| | | | | | "CD Features" = 1599
| | | | | | "IOProbeScore" = 5200
| | | | | | "Peripheral Device Type" = 5
| | | | | | "BD Features" = 0
| | | | | | "IOMatchCategory" = "IODefaultMatchCategory"
| | | | | | "DVD Features" = 111
| | | | | | "IOKitDebug" = 65535
| | | | | | }
| | | | | |
| | | | | +-o IODVDServices <class IODVDServices, id 0x100000288, !registered, !matched, active, busy 0, retain 4>
| | | | | {
| | | | | "device-type" = "DVD"
| | | | | }
But IODVDServices is not setup or attached to the SCSITaskUserClientIniter nor the IODVDBlockStorageDriver. Is it likely I'm missing some sort of initialization method call in my subclass(es) or errors in my property list? The property list for my kext looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>OSBundleRequired</key>
<string>Root</string>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>com.work.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>KEXT</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>IOKitPersonalities</key>
<dict>
<key>IOSCSIPeripheralDeviceType05Filter</key>
<dict>
<key>Peripheral Device Type</key>
<integer>5</integer>
<key>CFBundleIdentifier</key>
<string>com.work.IOSCSIMultimediaCommandsDeviceFilter</string>
<key>IOClass</key>
<string>IOSCSIPeripheralDeviceType05Filter</string>
<key>IOProviderClass</key>
<string>IOSCSIPeripheralDeviceNub</string>
<key>IOProbeScore</key>
<integer>200</integer>
<key>IOKitDebug</key>
<integer>65535</integer>
</dict>
</dict>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2013 Guy Helmer. All rights reserved.</string>
<key>OSBundleLibraries</key>
<dict>
<key>com.apple.iokit.IOSCSIMultimediaCommandsDevice</key>
<string>3.5.5</string>
<key>com.apple.kpi.mach</key>
<string>8.9.9</string>
<key>com.apple.kpi.libkern</key>
<string>8.9.9</string>
<key>com.apple.kpi.iokit</key>
<string>8.9.9</string>
<key>com.apple.kpi.bsd</key>
<string>8.9.9</string>
<key>com.apple.iokit.IOStorageFamily</key>
<string>1.0.0</string>
<key>com.apple.iokit.IOSCSIArchitectureModelFamily</key>
<string>1.0.0</string>
<key>com.apple.iokit.IODVDStorageFamily</key>
<string>1.0.0</string>
<key>com.apple.iokit.IOCDStorageFamily</key>
<string>1.0.0</string>
<key>com.apple.iokit.IOBDStorageFamily</key>
<string>1.5.0</string>
</dict>
</dict>
</plist>
Any thoughts are appreciated.
Thanks,
Guy
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Darwin-kernel mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden