Ask how to control unix socket with apparmor

151 Views Asked by At

I am trying to control access of unix domain socket using apparmor. Looking at the document in the link below, it seems that control over unix socket is possible. https://manpages.ubuntu.com/manpages/lunar/en/man5/apparmor.d.5.html

I'm developing in an embedded environment and I'm using Abstract Unix domain socket for IPC between processes. The environment of my target is as follows, and basically, access to file open or r/w of the process is controlled well using apparmor.

  • Linux kernel version 5.17.0-gb9a16995c467
  • AppArmor parser version 3.0.3

Here's what I'm testing: There is an application called server and an application called client, and these two perform IPC using Abstract Unix domain socket as shown in the code below. (The code below is abbreviated for the sake of the question.)

server.c

#include "sys/types.h"
#include "sys/stat.h"
#include "sys/socket.h"
#include <unistd.h>
#include <stdint.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <linux/un.h>

int main(int argc, char **argv) {
    int server_sockfd, client_sockfd;
    int state;
    unsigned int client_len;
    struct sockaddr_un clientaddr, serveraddr;

    printf("[Server] Start \n");

     client_len = sizeof(clientaddr);    
     if ((server_sockfd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)) < 0) {
         perror("socket error : ");
         exit(0);
     }
     
    bzero(&serveraddr, sizeof(serveraddr));
    serveraddr.sun_family = AF_UNIX;
    snprintf(serveraddr.sun_path, UNIX_PATH_MAX, "#UDS_TEST");
    serveraddr.sun_path[0] = '\0';

    state = bind(server_sockfd , (struct sockaddr *)&serveraddr, sizeof(serveraddr));
    if (state == -1) {
        perror("bind error : ");
        exit(0);
    }

    state = listen(server_sockfd, 5);
    if (state == -1) {
        perror("listen error : ");
        exit(0);
    }

    memset(&clientaddr, 0x00, sizeof(clientaddr));
    client_sockfd = accept4(server_sockfd, (struct sockaddr *)&clientaddr, &client_len, SOCK_CLOEXEC);
        
    if (client_sockfd < 0) {
        perror("accept error : ");
        exit(0);
    }
    
    printf("accepted\n");
    sleep(5);
    
    close(client_sockfd);
    close(server_sockfd);

    printf("[Server] END \n");      
    return 0;
}

client.c

#include "sys/types.h"
#include "sys/stat.h"
#include "sys/socket.h"
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <errno.h>
#include <linux/un.h>

int main(void) {
    int client_len;
    int sockfd;
    struct sockaddr_un clientaddr;
        
    printf("[Client] Start \n");
    
    sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sockfd == -1) {
        perror("error : ");
        exit(0);
    }

    bzero(&clientaddr, sizeof(clientaddr));
    clientaddr.sun_family = AF_UNIX;
    snprintf(clientaddr.sun_path, UNIX_PATH_MAX, "#UDS_TEST");
    clientaddr.sun_path[0] = '\0';
    
    if (connect(sockfd, (struct sockaddr *)&clientaddr, sizeof(clientaddr)) < 0)    {
        perror("Connect error: ");
        exit(0);
    }
        
    printf("connected\n");
    sleep(5);

    close(sockfd);
    
    printf("[Client] End \n");
    exit(0);
}

What I want is that the server connects only to the client I specify, and rejects unspecified applications through apparmor.

For reference, after cross compiling the above code, creating a skelton profile for the server application in Target as shown below, and executing the server, the following log appears.

$ cat /etc/apparmor.d/test
#include <tunables/global>
/home/server {
}

$ apparmor_parser -rCvK /etc/apparmor.d/test 
Replacement succeeded for "/home/server".
 
$ aa-status apparmor module is loaded. 
1 profiles are loaded. 
0 profiles are in enforce mode. 
1 profiles are in complain mode.   
 /home/server 
0 profiles are in kill mode. 
0 profiles are in unconfined mode. 
0 processes have profiles defined. 
0 processes are in enforce mode. 
0 processes are in complain mode. 
0 processes are unconfined but have a profile defined. 
0 processes are in mixed mode. 0 processes are in kill mode.

$ dmesg 
[ 1828.385996] audit: type=1400 audit(1692950798.624:97):apparmor="STATUS" operation="profile_load" profile="unconfined" name="/home/server" pid=315 comm="apparmor_parser" 
[ 1867.734245] audit: type=1400 audit(1692950837.972:98): apparmor="ALLOWED" operation="open" profile="/home/server" name="/lib/libc-2.31.so" pid=317 comm="server" requested_mask="r" denied_mask="r" fsuid=0 ouid=0 
[ 1867.739276] audit: type=1400 audit(1692950837.972:99): apparmor="ALLOWED" operation="file_mmap" profile="/home/server" name="/lib/libc-2.31.so" pid=317 comm="server" requested_mask="rm" denied_mask="rm" fsuid=0 ouid=0

I use aa-logprof to update the profile as shown below, and when I run it again, there is no apparmor log.

$ cat /etc/apparmor.d/test
#include <tunables/global>

/home/server {
   owner /lib/libc-2.31.so mr,
}

$ apparmor_parser -rCvK /etc/apparmor.d/test
Replacement succeeded for "/home/server".

<Run server and client in separate consoles.>


$ dmesg
<Nothing..>

$ aa-status
apparmor module is loaded.
1 profiles are loaded.
0 profiles are in enforce mode.
1 profiles are in complain mode.
   /home/server
0 profiles are in kill mode.
0 profiles are in unconfined mode.
0 processes have profiles defined.
0 processes are in enforce mode.
0 processes are in complain mode.
0 processes are unconfined but have a profile defined.
0 processes are in mixed mode.
0 processes are in kill mode.

Looking at the man page above, it seems possible, but I'm not familiar with the apparmor profile, so I'm asking. If anyone knows, please reply.

0

There are 0 best solutions below