Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

onvif "ter" namespace url empty resulting in ter:NotAuthorized QNAME exception #402

Open
zebity opened this issue Mar 8, 2024 · 4 comments

Comments

@zebity
Copy link
Contributor

zebity commented Mar 8, 2024

Issue

I have found when you get authentication failure when using camera with ws-security (ws-usernameToken) rather than http/ DIGEST the resulting "ter:NotAuthorized" return in the SOAP:Fault payload is undefined, and so results in invalid QName in generated CXF java code:

java.lang.RuntimeException: Invalid QName in mapping: ter:NotAuthorized
	at org.apache.cxf.helpers.DOMUtils.createQName(DOMUtils.java:461)
	at org.apache.cxf.binding.soap.interceptor.Soap12FaultInInterceptor.unmarshalFault(Soap12FaultInInterceptor.java:119)
	at org.apache.cxf.binding.soap.interceptor.Soap12FaultInInterceptor.handleMessage(Soap12FaultInInterceptor.java:66)
	at org.apache.cxf.binding.soap.interceptor.Soap12FaultInInterceptor.handleMessage(Soap12FaultInInterceptor.java:52)
	at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:307)
	at org.apache.cxf.interceptor.AbstractFaultChainInitiatorObserver.onMessage(AbstractFaultChainInitiatorObserver.java:112)
	at org.apache.cxf.binding.soap.interceptor.CheckFaultInterceptor.handleMessage(CheckFaultInterceptor.java:70)
	at org.apache.cxf.binding.soap.interceptor.CheckFaultInterceptor.handleMessage(CheckFaultInterceptor.java:35)
	at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:307)
	at org.apache.cxf.endpoint.ClientImpl.onMessage(ClientImpl.java:829)
	at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponseInternal(HTTPConduit.java:1760)
	at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponse(HTTPConduit.java:1626)
	at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.close(HTTPConduit.java:1420)
	at org.apache.cxf.transport.AbstractConduit.close(AbstractConduit.java:56)
	at org.apache.cxf.transport.http.HTTPConduit.close(HTTPConduit.java:717)
	at org.apache.cxf.transport.http.HttpClientHTTPConduit.close(HttpClientHTTPConduit.java:112)
	at org.apache.cxf.interceptor.MessageSenderInterceptor$MessageSenderEndingInterceptor.handleMessage(MessageSenderInterceptor.java:63)
	at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:307)
	at org.apache.cxf.endpoint.ClientImpl.doInvoke(ClientImpl.java:528)
	at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:439)
	at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:354)
	at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:312)
	at org.apache.cxf.frontend.ClientProxy.invokeSync(ClientProxy.java:96)
	at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:140)
	at jdk.proxy2/jdk.proxy2.$Proxy111.GetDeviceInformation(Unknown Source)
	at onvif_relay.relay.invokers.CheckClockSyncAndAccess.checkAccess(CheckClockSyncAndAccess.java:67)
	at com.ericsson.lift.sensoraasagent.services.DiscoveryService.startDeviceCredentialsCheck(DiscoveryService.java:365)
	at com.ericsson.lift.sensoraasagent.services.DiscoveryService.discoverDevices(DiscoveryService.java:149)
	at jdk.internal.reflect.GeneratedMethodAccessor345.invoke(Unknown Source)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:352)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:713)
	at com.ericsson.lift.sensoraasagent.services.DiscoveryService$$SpringCGLIB$$0.discoverDevices(<generated>)
	at com.ericsson.lift.sensoraasagent.schedulers.ScheduledTasks.executeAgent(ScheduledTasks.java:38)
	at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
	at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:96)
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
	at java.base/java.lang.Thread.run(Thread.java:840)

Diagnosis

I believe this is because the "ter" namesspace ONVIF Core - http://www.onvif.org/ver10/error , points to non-existent schema file.

I believe this should have definition of set of valid error such as "NotAuthorized" to avoid the invalid QName exception which will then allow catch of the correct Java SOAPFaultException (currently this doe not get back the valid QName listl

See this Java code snippet from: https://github.com/zebity/onvif-relay/blob/main/onvif-cxf-relay/src/main/java/onvif_relay/relay/invokers/CheckClockSyncAndAccess.java

try {

  	    authFault = false;
  	
        callo = JsonRequestResponse.create(call);
    
        Object[] proxy = onvifop.createSEI(callo, ctrl);
    
  	    GetDeviceInformationResponse resp = new GetDeviceInformationResponse();
  	    Holder<String> man = new Holder<>();
  	    Holder<String> mod = new Holder<>();
  	    Holder<String> firm = new Holder<>();
  	    Holder<String> serial = new Holder<>();
  	    Holder<String> hard = new Holder<>();
  	    ((Device)proxy[2]).GetDeviceInformation(man, mod, firm, serial, hard);
  	    resp.setManufacturer(man.value.toString());
  	    resp.setModel(mod.value.toString());
  	    resp.setFirmwareVersion(firm.value.toString());
  	    resp.setSerialNumber(serial.value.toString());
  	    resp.setHardwareId(hard.value.toString());
  	    callo.response = resp;
  	    res = new Object[3];
  	    res[0] = ctrl.get("security");
  	    res[1] = callo.ser();
  	    res[2] = resp;

      } catch (SOAPFaultException sex) {
        Iterator<QName> it = sex.getFault().getFaultSubcodes();
        if (it.hasNext()) {
          QName err = it.next();
        }
        if (sex.getMessage().contains("NotAuthorized")) {
          authFault = true;
          if (! altAuth) {
            altAuth = true;
            ctrl.put("security", "ws-security");
          } else {
        	res = new Object[3];
          	res[0] = ctrl.get("security");
          	res[1] = callo.ser();
          	res[2] = sex.getMessage();
            altAuth = false;
          }
        }
      } catch (WebServiceException wex) {
        // Assume digest with wrong password
      	res = new Object[3];
      	res[0] = ctrl.get("security");
      	res[1] = callo.ser();
      	res[2] = "Authorization loop detected: Invalid user/password";
      } catch (Exception ex) {
        ex.printStackTrace();
      }

Resolution

The solution is to reinstate "http://www.onvif.org/ver10/error" content in the source tree with correct QName definitions.

Could people better versed in SOAP/ONVIF please provide feedback.

@manoj-47
Copy link

manoj-47 commented Apr 1, 2024

+1

facing the same error

@HansBusch
Copy link
Member

Most probable cause for the failure is that the device doesn't include the namespace definition for prefix 'ter'. Some parser need it to correctly map QNames. Would be helpful to see the fault xml instance diagram.

@zebity
Copy link
Contributor Author

zebity commented May 21, 2024

HI @HansBusch ,

could I clarify what you are saying please.

When you say "the device doesn't include the namespace definition for prefix 'ter'", are you indicating the the Service Endpoint Interface (SEI) should:

  • be generated based on WSDL exposed via the particular device,
  • rather than based on WSDL that is published at www.onvif.org ?

This would then mean that responsibility for creation of "ter" namespace would then be via the device and not via the published specification.

I can see this would be a way of ensuring that the onvif specification is locked at a per device level, but this would make communications much more complex for the onvif client.

Also could you clarify what you mean by the "fault xml instance diagram".

My understanding is that the potential SOAP faults returned (and hence QNAMEs) are as per "ONVIF ONVIF Core Specification" section "5.8.2.2 Generic faults".

Thank you for review.

@HansBusch
Copy link
Member

Please provide the xml response document to see whether it contains the namespace definition for prefix ter.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants