So far this question has only had 7 views, and earnt me the "Tumbleweed" badge, but nevertheless I thought I'd follow up with an explanation of my final solution.
Using the CatAdm object really was the only option in the end, because that's the only way to work around the bug in Indexining Service related to aliases (mentioned in my original post).
One method (which is relatively easy in ASP.NET, and possible in ASP Classic with the use of a custom COM component of equivalent) would be to use impersonation: use a privileged account to read the list of scopes from the CatAdm object, then use the account of the authorized HTTP request to do a query on those scopes. The results will contain only the directories which that account has access to.
The problem is that only administrator accounts have permission to use the CatAdm object, and using an administrator account to serve HTTP requests is not good practice, from a security point of view.
So, although it adds to the administration burden, I decided to write a separate HTA script that must be run (on the server machine itself, not via HTTP) any time that directories are added or removed from the catalog. The script reads the list of scopes from the CatAdm object and writes it to a configuration file:
Function makeConfig(catalogName)
Set machine = CreateObject("Shell.LocalMachine")
Set adm = CreateObject("Microsoft.ISAdm")
Set cat = adm.GetCatalogByName(catalogName)
Dim config
config = "<%" & vbCrLf
config = config & "' Automatically generated by " & document.location.pathname & " at " & Now & vbCrLf
config = config & "' This file is indended for inclusion by the intranet search script." & vbCrLf
config = config & "catalogMachine = """ & machine.MachineName & """" & vbCrLf
config = config & "catalogName = """ & catalogName & """" & vbCrLf
scopeFound = cat.FindFirstScope()
While scopeFound
Set scope = cat.GetScope()
If Not scope.ExcludeScope Then
' Must be lowercase because query results are returned in lowercase
dir = lcase(scope.Path)
If scope.Alias <> "" Then
alias = scope.Alias
Else
alias = scope.Path
End If
config = config & "dirs(""" & dir & """) = """ & alias & """" & vbCrLf
End If
scopeFound = cat.FindNextScope()
Wend
config = config & "%>" & vbCrLf
makeConfig = config
End Function
Then the search script itself just reads the configuration file and uses this to find the list of accessible directories. To work around the Indexing Service bug, it's necessary to map from the physical directory to the alias:
Set dirs = CreateObject("Scripting.Dictionary")
%><!--#include file="search_config.asp"--><%
catalogURI = "query://" & catalogMachine & "/" & catalogName
queryString = ""
For Each dir In dirs
If queryString <> "" Then
queryString = queryString & " or "
End If
queryString = queryString & "@Path = """ & dir & """"
Next
' But the @Path attribute is not indexed, and running queryString
' as is will return no results. Solution: limit search to only
' directories, i.e. items with the 0x10 flag set in @Attrib.
queryString = "@Attrib ^a 0x10 and (" & queryString & ")"
' No point asking for sorted query results, because we need
' to map the results from real paths to network aliases and
' sort again ourselves.
Set query = Server.CreateObject("ixsso.Query")
query.Catalog = catalogURI
query.Query = queryString
query.Columns = "path"
query.MaxRecords = dirs.Count
Set rs = query.CreateRecordSet("sequential")
i = 0
Do While Not rs.EOF
ReDim Preserve accessibleAliases(i)
accessibleAliases(i) = dirs(rs("path").Value)
i = i + 1
rs.MoveNext
Loop
rs.Close
BubbleSort accessibleAliases