Bug 52

Summary: DICOM level security association problems
Product: Orthanc Reporter: Sébastien Jodogne <s.jodogne>
Component: Orthanc CoreAssignee: Sébastien Jodogne <s.jodogne>
Status: RESOLVED FIXED    
Severity: normal    
Priority: ---    
Version: unspecified   
Hardware: All   
OS: All   

Description Sébastien Jodogne 2020-06-29 15:12:38 CEST
[BitBucket user: Денис Смирнов]
[BitBucket date: 2017-06-21.07:29:51]

I found out that Orthanc 1.2.0 allows dicom associations with nodes not mentioned in DicomModalities.

### ECHO

It seems Orthanc only cares about ip and port of incomming dicom connection (but doesn't check AEC) during ECHO. Somehow wrong AEC and AET passes association.

```
#!bash

denis@brewery:~$ echoscu -aet TEST_WRONG_AET -aec ORTHANC_WRONG_AEC pacs.viveya.local 4242 -v
I: Requesting Association
I: Association Accepted (Max Send PDV: 16372)
I: Sending Echo Request (MsgID 1)
I: Received Echo Response (Success)
I: Releasing Association
```


### FIND

When I use FIND command client's ip address and port doesn't matter. I just need a valid AET from Orthanc's DicomModalities list in orthanc.json. In the examples below I use `findscu` from a computer that is not mentioned in DicomModalities

* Wrong ip, wrong AET - all ok

```
#!bash

findscu -S -k QueryRetrieveLevel=STUDY -k StudyDate=20160805 -k StudyDescription -k StudyInstanceUID -aec ORTHANC -aet WRONG_AET pacs.viveya.local 4242
E: Find Failed, query keys:
E: 
E: # Dicom-File-Format
E: 
E: # Dicom-Meta-Information-Header
E: # Used TransferSyntax: Little Endian Explicit
E: 
E: # Dicom-Data-Set
E: # Used TransferSyntax: Little Endian Explicit
E: (0008,0020) DA [20160805]                               #   8, 1 StudyDate
E: (0008,0052) CS [STUDY ]                                 #   6, 1 QueryRetrieveLevel
E: (0008,1030) LO (no value available)                     #   0, 0 StudyDescription
E: (0020,000d) UI (no value available)                     #   0, 0 StudyInstanceUID
E: 
E: 0006:0317 Peer aborted Association (or never connected)
```

* Wrong ip, valid AET from DicomModalities for some other host - not ok

```
#!bash

findscu -S -k QueryRetrieveLevel=STUDY -k StudyDate=20160805 -k StudyDescription -k StudyInstanceUID -aec ORTHANC -aet GUINNESS pacs.viveya.local 4242
I: Find Response: 1 (Pending)
I: 
I: # Dicom-Data-Set
I: # Used TransferSyntax: Little Endian Explicit
I: (0008,0005) CS [ISO_IR 100]                             #  10, 1 SpecificCharacterSet
I: (0008,0020) DA [20160805]                               #   8, 1 StudyDate
I: (0008,0052) CS [STUDY ]                                 #   6, 1 QueryRetrieveLevel
I: (0008,1030) LO [HEAD]                                   #   4, 1 StudyDescription
I: (0020,000d) UI [1.2.840.113619.2.334.3.2831165804.922.1470213875.358] #  52, 1 StudyInstanceUID
```

###STORE

I can save dicom image to Orthanc from a host and AET not mentioned in DicomModalities
```
#!bash

denis@brewery:~$ storescu pacs.viveya.local 4242 xr_hands.dcm -aec ORTHANC -aet WRONG_AET -v
I: checking input files ...
I: Requesting Association
I: Association Accepted (Max Send PDV: 16372)
I: Sending file: xr_hands.dcm
I: Converting transfer syntax: Little Endian Explicit -> Little Endian Explicit
I: Sending Store Request (MsgID 1, CR)
XMIT: .....................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
I: Received Store Response (Success)
I: Releasing Association
```
Comment 1 Sébastien Jodogne 2020-06-29 15:20:33 CEST
[BitBucket user: Sébastien Jodogne]
[BitBucket date: 2017-07-18.14:35:59]

To forbid connections where the `-aec` (Application Entity Title of the remote DICOM server) does not correspond to the AET of Orthanc, the [configuration option](http://book.orthanc-server.com/users/configuration.html) `DicomCheckCalledAet` has to be set to `true`. Here is a sample session with this option enabled:

```
$ echoscu -v localhost 4242 -aec ORTHANC
I: Requesting Association
I: Association Accepted (Max Send PDV: 16372)
I: Sending Echo Request (MsgID 1)
I: Received Echo Response (Success)
I: Releasing Association

$ echoscu -v localhost 4242 -aec WRONG_ORTHANC
I: Requesting Association
F: Association Rejected:
F: Result: Rejected Permanent, Source: Service User
F: Reason: Called AE Title Not Recognized

$ findscu -P -k QueryRetrieveLevel=STUDY localhost 4242 -aec WRONG_ORTHANC
E: Association Rejected:
E: Result: Rejected Permanent, Source: Service User
E: Reason: Called AE Title Not Recognized

$ storescu localhost 4242 xr_hands.dcm -aec WRONG_ORTHANC
F: Association Rejected:
F: Result: Rejected Permanent, Source: Service User
F: Reason: Called AE Title Not Recognized
```

The `DicomCheckCalledAet` configuration option has been around since the very first releases of Orthanc. I will discuss the `-aet` (AET of the local DICOM client) part together with the IP addresses below.
Comment 2 Sébastien Jodogne 2020-06-29 15:20:35 CEST
[BitBucket user: Sébastien Jodogne]
[BitBucket date: 2017-07-18.15:33:31]

Fix issue 52 (DICOM level security association problems)

→ https://hg.orthanc-server.com/orthanc/changeset/3ab96768d144
Comment 3 Sébastien Jodogne 2020-06-29 15:20:36 CEST
[BitBucket user: Sébastien Jodogne]
[BitBucket date: 2017-07-18.15:46:35]

The philosophy of Orthanc consists in making configuration as simple as possible for basic use cases. I have therefore added [two new security-related configuration options](https://hg.orthanc-server.com/orthanc/raw-file/default/Resources/Configuration.json) to solve this issue: `DicomAlwaysAllowStore` and `DicomCheckModalityHost`. The former forbids C-Store from unknown modalities, the second checks the IP address of the remote modality.

Here is a sample, minimalist configuration file illustrating those 2 options:

``` json
{
  "DicomCheckCalledAet" : true,
  "DicomModalities" : {
    "peer" : [ "PEER", "127.0.0.1", 2000 ],
    "peer2" : [ "BAD_IP", "1.1.1.1", 2000 ]
  },
  "DicomAlwaysAllowStore" : false,
  "DicomCheckModalityHost" : true
}
```

Here is now an interactive session (run from the localhost `127.0.0.1`) that shows how the security now works as you initially expected:

``` text
$ echoscu -aec ORTHANC -aet PEER localhost 4242 -v 2>&1 | tail -n2
I: Received Echo Response (Success)
I: Releasing Association

$ echoscu -aec ORTHANC -aet BAD_AET localhost 4242 -v 2>&1 | tail -n2
E: Echo Failed: 0006:0317 Peer aborted Association (or never connected)
I: Peer Aborted Association

$ echoscu -aec ORTHANC -aet BAD_IP localhost 4242 -v 2>&1 | tail -n2
E: Echo Failed: 0006:0317 Peer aborted Association (or never connected)
I: Peer Aborted Association

$ findscu -S -k QueryRetrieveLevel=STUDY -aec ORTHANC -aet PEER localhost 4242 -v 2>&1 | tail -n2
I: Received Final Find Response (Success)
I: Releasing Association

$ findscu -S -k QueryRetrieveLevel=STUDY -aec ORTHANC -aet BAD_AET localhost 4242 -v 2>&1 | tail -n2
E: 0006:0317 Peer aborted Association (or never connected)
I: Peer Aborted Association

$ findscu -S -k QueryRetrieveLevel=STUDY -aec ORTHANC -aet BAD_IP localhost 4242 -v 2>&1 | tail -n2
E: 0006:0317 Peer aborted Association (or never connected)
I: Peer Aborted Association

$ storescu localhost 4242 /tmp/xr_hands.dcm -aec ORTHANC -aet PEER -v 2>&1 | tail -n2
I: Received Store Response (Success)
I: Releasing Association

$ storescu localhost 4242 /tmp/xr_hands.dcm -aec ORTHANC -aet BAD_AET -v 2>&1 | tail -n2
I: Aborting Association
E: Association Abort Failed: 0006:031d TCP I/O Error (Broken pipe) occurred in routine: sendAbortTCP

$ storescu localhost 4242 /tmp/xr_hands.dcm -aec ORTHANC -aet BAD_IP -v 2>&1 | tail -n2
I: Aborting Association
E: Association Abort Failed: 0006:031d TCP I/O Error (Broken pipe) occurred in routine: sendAbortTCP
```

The log file of Orthanc shows that `BAD_AET` and `BAD_IP` are treated differently:

```
W0718 17:43:13.212854 OrthancInitialization.cpp:865] Modality "BAD_AET" is not listed in the "DicomModalities" configuration option
W0718 17:43:13.212882 CommandDispatcher.cpp:812] Rejected Echo request from remote DICOM modality with AET "BAD_AET" and hostname "127.0.0.1"
W0718 17:43:14.458409 OrthancInitialization.cpp:876] Forbidding access from AET "BAD_IP" given its hostname (127.0.0.1) does not match the "DicomModalities" configuration option (1.1.1.1 was expected)
W0718 17:43:14.458440 CommandDispatcher.cpp:812] Rejected Echo request from remote DICOM modality with AET "BAD_IP" and hostname "127.0.0.1"
```
Comment 4 Sébastien Jodogne 2020-06-29 15:20:47 CEST
[BitBucket user: Sébastien Jodogne]
[BitBucket date: 2017-08-25.14:45:48]

For further reference: In the mainline of Orthanc and for releases >= 1.3.1, configuration option `DicomAlwaysAllowEcho` will have to be set to `false` to protect C-Echo from arising from unknown modalities.

Here is the related changeset: https://hg.orthanc-server.com/orthanc/changeset/3ec85ff48374803e0e796ba8bf0923f53f591e4b