17 Replies
      Latest reply on Oct 21, 2019 3:17 AM by liang.zhou
      liang.zhou Level 1 Level 1 (0 points)

        Hello,

        I'd like to know if there is a way to get this properties(like the image below) of a USB thumb drive from a mount point(/Volumes/mydrive) in a generic kernel extension? Thanks!

         

        https://www.dropbox.com/s/5ykusvrlz7apzzs/IORegistryExplorer.png?raw=1

        • Re: Getting vender id from mount point
          john daniel Level 4 Level 4 (500 points)

          I don't know anything about kernel extensions. This is just IOKit data. I assume you can get that from anywhere.

            • Re: Getting vender id from mount point
              liang.zhou Level 1 Level 1 (0 points)

              I can use DiskArbitration and IOKit frameworks to get vender id in user mode, but I cannot use the same code in kernel extension.

              This is my code.

               

              int GetVenderId(DADiskRef dsk, int *vid)
              {
                  io_service_t ioService;
                  CFTypeRef vendorid;
                 
                  ioService = DADiskCopyIOMedia(dsk);
                  
                  if (0 != ioService)
                  {
                      vendorid = IORegistryEntrySearchCFProperty(ioService, kIOServicePlane, CFSTR("idVendor"), NULL, kIORegistryIterateParents | kIORegistryIterateRecursively);
                      if(0 != vendorid)
                      {
                          if (CFNumberGetValue(vendorid, kCFNumberIntType, vid))
                          {
                              return 0;
                          }
                          
                          CFRelease(vendorid);
                      }
                      
                      IOObjectRelease(ioService);
                  }
                  
                  return 1;
              }
              
              
            • Re: Getting vender id from mount point
              eskimo Apple Staff Apple Staff (12,305 points)

              Clearly your KEXT doesn’t have a DADiskRef because DiskArb is a user-space-only API.  So what data is your KEXT starting with?

              Share and Enjoy

              Quinn “The Eskimo!”
              Apple Developer Relations, Developer Technical Support, Core OS/Hardware
              let myEmail = "eskimo" + "1" + "@apple.com"

                • Re: Getting vender id from mount point
                  liang.zhou Level 1 Level 1 (0 points)

                  You're right. I'm using MAC framework in legacy code of a general kernel extension. I need to block some USB drives, but the only parameter I can use in callback function is a vnode pointer. Is it possible to get device vid starting from that? Or is there any kernel-mode IOKit API I can use to get that information? I have no idea whether it's safe to mix BSD and IOKit code together. I don't want to change the entire project from c to c++. Thanks!

                    • Re: Getting vender id from mount point
                      eskimo Apple Staff Apple Staff (12,305 points)

                      I'm using MAC framework in legacy code of a general kernel extension.

                      Just so we’re clear, the kernel’s MAC framework is not considered a supported KPI.  See QA1574 Kernel's MAC framework.

                      Having said that, your actual question (how to map a vnode to I/O Registry properties) should be possible with supported KPIs.  The process runs something like this:

                      1. Call vnode_mount to map the vnode_t to a mount_t.

                      2. Call vfs_statfs to map the mount_t to a struct vfsstatfs *.

                      3. Look at the f_mntfromname member.  This is the name of the dev node that the volume is mounted on.

                      From there things proceed much like they would in user space:

                      1. Use I/O Registry KPIs to find the IOMedia node for that volume.

                      2. Walk up the I/O Registry service plane to find the node you care about.

                      3. Extract properties from there.

                      Share and Enjoy

                      Quinn “The Eskimo!”
                      Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                      let myEmail = "eskimo" + "1" + "@apple.com"

                        • Re: Getting vender id from mount point
                          liang.zhou Level 1 Level 1 (0 points)

                          Thanks a lot. I have one more little question. Is there a way to differentiate among these these three types of mount point: removable drive(thumb drive, harddisk), CD/DVD drive and network share(SMB, AFP), regardless of the their connection types. My first thought is to check the file system name, but there are too many types out there. Is there a reliable way to get this information? Thanks!

                            • Re: Getting vender id from mount point
                              eskimo Apple Staff Apple Staff (12,305 points)

                              removable drive (thumb drive, harddisk), CD/DVD drive and network share (SMB, AFP), regardless of the their connection types.

                              This is a tricky business.  Part of the problem is one of definition.  Let’s say I have a DVD mounted over iSCSI.  Is that a DVD drive?  Or a network drive?  It kinda depends on how you look at it.  It’s a DVD drive in terms of it drive access semantics but it’s a network drive in terms of latency and reliability.

                              Note This is not a theoretical example.  For a long time macOS supported System Preferences > Sharing > DVD or CD Sharing.

                              Anyway, to start you can distinguish between network volumes and non-network volumes by looking at the MNT_LOCAL flag.  In general, a volume with MNT_LOCAL set is expected to have a mount-from value (for example, f_mntfromname in statfs) that leads to a dev node.

                              Be warned:

                              • Local volumes can actually be running over the network, as in the iSCSI example I gave above.

                              • Non-local volumes are often used for completely virtual file systems, like the procfs example in Fuse for macOS.

                              • Even for a local volume, the mount-from value may be virtual (for example, a disk image).

                              Beyond that, things get even trickier.  You can use the mount-from value on a local volume to get to an I/O Registry entry, and then monkey around in the I/O Registry looking for key information about the volume.  You will end up with a bunch of special cases, and those special cases will have to be updated over time.

                              My general advice would be to bounce out to user space and ask DiskArbitration about the volume.  It already calculates the info, so relying on it saves you a bunch of work and ensures you use the same values as the rest of the OS.

                              Share and Enjoy

                              Quinn “The Eskimo!”
                              Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                              let myEmail = "eskimo" + "1" + "@apple.com"

                                • Re: Getting vender id from mount point
                                  liang.zhou Level 1 Level 1 (0 points)

                                  Thanks a lot! I decide to check the volume type in user space. Could you please take a look at my code. Is it correct?

                                   

                                  Utility::VOLUME_TYPE Utility::getVolumeType(const char *path)
                                  {
                                      Utility::VOLUME_TYPE type = Utility::VOLUME_TYPE::UNKNOWN;
                                      
                                      DASessionRef session;
                                      CFURLRef url;
                                      DADiskRef disk;
                                      CFDictionaryRef dict;
                                      DADiskRef wholeDisk;
                                      io_service_t mediaService;
                                      
                                      session = DASessionCreate(NULL);
                                      if (session)
                                      {
                                          url = CFURLCreateFromFileSystemRepresentation(NULL, (const UInt8 *)path, strlen(path), TRUE);
                                          if (url)
                                          {
                                              disk = DADiskCreateFromVolumePath(NULL, session, url);
                                              CFRelease(url);
                                              
                                              if (disk)
                                              {
                                                  dict = DADiskCopyDescription(disk);
                                                  
                                                  CFBooleanRef type_key = (CFBooleanRef)CFDictionaryGetValue(dict, kDADiskDescriptionVolumeNetworkKey);
                                                  if (type_key && CFBooleanGetValue(type_key))
                                                  {
                                                      type = Utility::VOLUME_TYPE::NETWORK;
                                                  }
                                                  else
                                                  {
                                                      type_key = (CFBooleanRef)CFDictionaryGetValue(dict, kDADiskDescriptionMediaRemovableKey);
                                                      if (type_key)
                                                      {
                                                          type = CFBooleanGetValue(type_key) ? Utility::VOLUME_TYPE::REMOVABLE : Utility::VOLUME_TYPE::INTERNAL;
                                                          
                                                          wholeDisk = DADiskCopyWholeDisk(disk);
                                                          if (wholeDisk)
                                                          {
                                                              mediaService = DADiskCopyIOMedia(wholeDisk);
                                                              if (mediaService)
                                                              {
                                                                  if (IOObjectConformsTo(mediaService, kIOCDMediaClass)
                                                                      || IOObjectConformsTo(mediaService, kIODVDMediaClass))
                                                                  {
                                                                      type = Utility::VOLUME_TYPE::OPTICAL;
                                                                  }
                                                                  IOObjectRelease(mediaService);
                                                              }
                                                              CFRelease(wholeDisk);
                                                          }
                                                      }
                                                  }
                                                  CFRelease(dict);
                                                  CFRelease(disk);
                                              }
                                          }
                                          CFRelease(session);
                                      }
                                      
                                      return type;
                                  }
                                  
                                  
                                    • Re: Getting vender id from mount point
                                      eskimo Apple Staff Apple Staff (12,305 points)

                                      I decide to check the volume type in user space.

                                      Wise choice!

                                      Could you please take a look at my code. Is it correct?

                                      You can do this more easily by calling statfs and then passing f_mntfromname to DADiskCreateFromBSDName.

                                      Also, it’s a lot easier to wrangle dictionaries from Objective-C (or Objective-C++, if you’re so inclined).  For example:

                                      struct statfs stfs;
                                      
                                      int err = statfs("/", &stfs);
                                      …  check error …
                                      
                                      if ( !(stfs.f_flags & MNT_LOCAL) ) {
                                          … bail out …
                                      }
                                      
                                      DASessionRef session = DASessionCreate(NULL);
                                      CFAutorelease(session);
                                      
                                      DADiskRef disk = DADiskCreateFromBSDName(NULL, session, stfs.f_mntfromname);
                                      CFAutorelease(disk);
                                      
                                      NSDictionary * diskInfo = CFBridgingRelease( DADiskCopyDescription(disk) );
                                      BOOL isRemovable = [diskInfo[(__bridge NSString *) kDADiskDescriptionMediaRemovableKey] boolValue];
                                      NSLog(@"removable: %@", isRemovable ? @"YES" : @"NO");

                                      Share and Enjoy

                                      Quinn “The Eskimo!”
                                      Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                                      let myEmail = "eskimo" + "1" + "@apple.com"

                                        • Re: Getting vender id from mount point
                                          liang.zhou Level 1 Level 1 (0 points)

                                          Thanks a lot!

                                          • Re: Getting vender id from mount point
                                            liang.zhou Level 1 Level 1 (0 points)

                                            I found a problem when testing my kext. I want to block sensitive files being copied to external drives. For removable drives and network shares, I can get the file path and from there get  f_mntfromname and f_mnttoname. But for DVD drive, I cannot prevent burning sensitive information on it. When a blank disc in the drive, there is no file system on it, so there is no volume mounted for that drive, only a raw disk. When the burning process starts, the mpo_vnode_check_open callback function is not called, or I don't register the correct callback. Is it possible to capture this event(file writing) using MAC framework? Thanks!

                                              • Re: Getting vender id from mount point
                                                eskimo Apple Staff Apple Staff (12,305 points)

                                                Is it possible to capture this event (file writing) using MAC framework?

                                                As I mentioned earlier, the kernel’s MAC framework is not a supported KPI.

                                                Share and Enjoy

                                                Quinn “The Eskimo!”
                                                Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                                                let myEmail = "eskimo" + "1" + "@apple.com"

                                                  • Re: Getting vender id from mount point
                                                    liang.zhou Level 1 Level 1 (0 points)

                                                    Is it possible to block DVD burning using any public KPI, like Kauth? Regardless of the KPI I use, there seems to be no file writing operation I can hook and block.

                                                      • Re: Getting vender id from mount point
                                                        eskimo Apple Staff Apple Staff (12,305 points)

                                                        Is it possible to block DVD burning using any public KPI, like Kauth?

                                                        Probably not.

                                                        Rather, I’d approach this by preventing the user from seeing the blank DVD in the first place.  There’s two ways you might achieve that:

                                                        • In kernel space, you might be able to use I/O Kit to match ‘harder’ on the I/O Registry nubs and thus prevent the built-in DVD infrastructure from matching.

                                                        • In user space, you might be able to use DiskArbitration to claim the blank DVD and thus prevent the system from using it.

                                                        Share and Enjoy

                                                        Quinn “The Eskimo!”
                                                        Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                                                        let myEmail = "eskimo" + "1" + "@apple.com"