1d9f0492fSopenharmony_ci/*
2d9f0492fSopenharmony_ci* Copyright (c) 2021 Huawei Device Co., Ltd.
3d9f0492fSopenharmony_ci* Licensed under the Apache License, Version 2.0 (the "License");
4d9f0492fSopenharmony_ci* you may not use this file except in compliance with the License.
5d9f0492fSopenharmony_ci* You may obtain a copy of the License at
6d9f0492fSopenharmony_ci*
7d9f0492fSopenharmony_ci*     http://www.apache.org/licenses/LICENSE-2.0
8d9f0492fSopenharmony_ci*
9d9f0492fSopenharmony_ci* Unless required by applicable law or agreed to in writing, software
10d9f0492fSopenharmony_ci* distributed under the License is distributed on an "AS IS" BASIS,
11d9f0492fSopenharmony_ci* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12d9f0492fSopenharmony_ci* See the License for the specific language governing permissions and
13d9f0492fSopenharmony_ci* limitations under the License.
14d9f0492fSopenharmony_ci*/
15d9f0492fSopenharmony_ci
16d9f0492fSopenharmony_ci#include "fd_holder_internal.h"
17d9f0492fSopenharmony_ci#include <errno.h>
18d9f0492fSopenharmony_ci#include <stdio.h>
19d9f0492fSopenharmony_ci#include "beget_ext.h"
20d9f0492fSopenharmony_ci#include "securec.h"
21d9f0492fSopenharmony_ci
22d9f0492fSopenharmony_ci#ifndef PAGE_SIZE
23d9f0492fSopenharmony_ci#define PAGE_SIZE (4096U)
24d9f0492fSopenharmony_ci#endif
25d9f0492fSopenharmony_ci
26d9f0492fSopenharmony_ciint BuildControlMessage(struct msghdr *msghdr,  int *fds, int fdCount, bool sendUcred)
27d9f0492fSopenharmony_ci{
28d9f0492fSopenharmony_ci    if (msghdr == NULL || (fdCount > 0 && fds == NULL)) {
29d9f0492fSopenharmony_ci        BEGET_LOGE("Build control message with invalid parameter");
30d9f0492fSopenharmony_ci        return -1;
31d9f0492fSopenharmony_ci    }
32d9f0492fSopenharmony_ci
33d9f0492fSopenharmony_ci    if (fdCount > 0) {
34d9f0492fSopenharmony_ci        msghdr->msg_controllen = CMSG_SPACE(sizeof(int) * fdCount);
35d9f0492fSopenharmony_ci    } else {
36d9f0492fSopenharmony_ci        msghdr->msg_controllen = 0;
37d9f0492fSopenharmony_ci    }
38d9f0492fSopenharmony_ci
39d9f0492fSopenharmony_ci    if (sendUcred) {
40d9f0492fSopenharmony_ci        msghdr->msg_controllen += CMSG_SPACE(sizeof(struct ucred));
41d9f0492fSopenharmony_ci    }
42d9f0492fSopenharmony_ci
43d9f0492fSopenharmony_ci    msghdr->msg_control = calloc(1, ((msghdr->msg_controllen == 0) ? 1 : msghdr->msg_controllen));
44d9f0492fSopenharmony_ci    BEGET_ERROR_CHECK(msghdr->msg_control != NULL, return -1, "Failed to build control message");
45d9f0492fSopenharmony_ci
46d9f0492fSopenharmony_ci    struct cmsghdr *cmsg = NULL;
47d9f0492fSopenharmony_ci    cmsg = CMSG_FIRSTHDR(msghdr);
48d9f0492fSopenharmony_ci    BEGET_ERROR_CHECK(cmsg != NULL, return -1, "Failed to build cmsg");
49d9f0492fSopenharmony_ci
50d9f0492fSopenharmony_ci    if (fdCount > 0) {
51d9f0492fSopenharmony_ci        cmsg->cmsg_level = SOL_SOCKET;
52d9f0492fSopenharmony_ci        cmsg->cmsg_type = SCM_RIGHTS;
53d9f0492fSopenharmony_ci        cmsg->cmsg_len = CMSG_LEN(sizeof(int) * fdCount);
54d9f0492fSopenharmony_ci        int ret = memcpy_s(CMSG_DATA(cmsg), cmsg->cmsg_len, fds, sizeof(int) * fdCount);
55d9f0492fSopenharmony_ci        BEGET_ERROR_CHECK(ret == 0, free(msghdr->msg_control);
56d9f0492fSopenharmony_ci            msghdr->msg_control = NULL;
57d9f0492fSopenharmony_ci            return -1, "Control message is not valid");
58d9f0492fSopenharmony_ci        // build ucred info
59d9f0492fSopenharmony_ci        cmsg = CMSG_NXTHDR(msghdr, cmsg);
60d9f0492fSopenharmony_ci    }
61d9f0492fSopenharmony_ci
62d9f0492fSopenharmony_ci    if (sendUcred) {
63d9f0492fSopenharmony_ci        BEGET_ERROR_CHECK(cmsg != NULL, free(msghdr->msg_control);
64d9f0492fSopenharmony_ci            msghdr->msg_control = NULL;
65d9f0492fSopenharmony_ci            return -1, "Control message is not valid");
66d9f0492fSopenharmony_ci
67d9f0492fSopenharmony_ci        struct ucred *ucred;
68d9f0492fSopenharmony_ci        cmsg->cmsg_level = SOL_SOCKET;
69d9f0492fSopenharmony_ci        cmsg->cmsg_type = SCM_CREDENTIALS;
70d9f0492fSopenharmony_ci        cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
71d9f0492fSopenharmony_ci        ucred = (struct ucred*) CMSG_DATA(cmsg);
72d9f0492fSopenharmony_ci        ucred->pid = getpid();
73d9f0492fSopenharmony_ci        ucred->uid = getuid();
74d9f0492fSopenharmony_ci        ucred->gid = getgid();
75d9f0492fSopenharmony_ci    }
76d9f0492fSopenharmony_ci    return 0;
77d9f0492fSopenharmony_ci}
78d9f0492fSopenharmony_ci
79d9f0492fSopenharmony_ciSTATIC int *GetFdsFromMsg(size_t *outFdCount, pid_t *requestPid, struct msghdr msghdr)
80d9f0492fSopenharmony_ci{
81d9f0492fSopenharmony_ci    if ((msghdr.msg_flags) & MSG_TRUNC) {
82d9f0492fSopenharmony_ci        BEGET_LOGE("Message was truncated when receiving fds");
83d9f0492fSopenharmony_ci        return NULL;
84d9f0492fSopenharmony_ci    }
85d9f0492fSopenharmony_ci
86d9f0492fSopenharmony_ci    struct cmsghdr *cmsg = NULL;
87d9f0492fSopenharmony_ci    int *fds = NULL;
88d9f0492fSopenharmony_ci    size_t fdCount = 0;
89d9f0492fSopenharmony_ci    for (cmsg = CMSG_FIRSTHDR(&msghdr); cmsg != NULL; cmsg = CMSG_NXTHDR(&msghdr, cmsg)) {
90d9f0492fSopenharmony_ci        if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
91d9f0492fSopenharmony_ci            fds = (int*)CMSG_DATA(cmsg);
92d9f0492fSopenharmony_ci            fdCount = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
93d9f0492fSopenharmony_ci            BEGET_ERROR_CHECK(fdCount <= MAX_HOLD_FDS, return NULL, "Too many fds returned.");
94d9f0492fSopenharmony_ci        }
95d9f0492fSopenharmony_ci        if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS &&
96d9f0492fSopenharmony_ci            cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
97d9f0492fSopenharmony_ci            // Ignore credentials
98d9f0492fSopenharmony_ci            if (requestPid != NULL) {
99d9f0492fSopenharmony_ci                struct ucred *ucred = (struct ucred*)CMSG_DATA(cmsg);
100d9f0492fSopenharmony_ci                *requestPid = ucred->pid;
101d9f0492fSopenharmony_ci            }
102d9f0492fSopenharmony_ci            continue;
103d9f0492fSopenharmony_ci        }
104d9f0492fSopenharmony_ci    }
105d9f0492fSopenharmony_ci    int *outFds = NULL;
106d9f0492fSopenharmony_ci    if (fds != NULL && fdCount > 0) {
107d9f0492fSopenharmony_ci        outFds = calloc(fdCount + 1, sizeof(int));
108d9f0492fSopenharmony_ci        BEGET_ERROR_CHECK(outFds != NULL, return NULL, "Failed to allocate memory for fds");
109d9f0492fSopenharmony_ci        BEGET_ERROR_CHECK(memcpy_s(outFds, sizeof(int) * (fdCount + 1), fds, sizeof(int) * fdCount) == 0,
110d9f0492fSopenharmony_ci            free(outFds); return NULL, "Failed to copy fds");
111d9f0492fSopenharmony_ci    }
112d9f0492fSopenharmony_ci    *outFdCount = fdCount;
113d9f0492fSopenharmony_ci    return outFds;
114d9f0492fSopenharmony_ci}
115d9f0492fSopenharmony_ci
116d9f0492fSopenharmony_ci// This function will allocate memory to store FDs
117d9f0492fSopenharmony_ci// Remember to delete when not used anymore.
118d9f0492fSopenharmony_ciint *ReceiveFds(int sock, struct iovec iovec, size_t *outFdCount, bool nonblock, pid_t *requestPid)
119d9f0492fSopenharmony_ci{
120d9f0492fSopenharmony_ci    CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred)) +
121d9f0492fSopenharmony_ci         CMSG_SPACE(sizeof(int) * MAX_HOLD_FDS)) control;
122d9f0492fSopenharmony_ci
123d9f0492fSopenharmony_ci    BEGET_ERROR_CHECK(sizeof(control) <= PAGE_SIZE, return NULL, "Too many fds, out of memory");
124d9f0492fSopenharmony_ci
125d9f0492fSopenharmony_ci    struct msghdr msghdr = {
126d9f0492fSopenharmony_ci        .msg_iov = &iovec,
127d9f0492fSopenharmony_ci        .msg_iovlen = 1,
128d9f0492fSopenharmony_ci        .msg_control = &control,
129d9f0492fSopenharmony_ci        .msg_controllen = sizeof(control),
130d9f0492fSopenharmony_ci        .msg_flags = 0,
131d9f0492fSopenharmony_ci    };
132d9f0492fSopenharmony_ci
133d9f0492fSopenharmony_ci    int flags = MSG_CMSG_CLOEXEC | MSG_TRUNC;
134d9f0492fSopenharmony_ci    if (nonblock) {
135d9f0492fSopenharmony_ci        flags |= MSG_DONTWAIT;
136d9f0492fSopenharmony_ci    }
137d9f0492fSopenharmony_ci    ssize_t rc = TEMP_FAILURE_RETRY(recvmsg(sock, &msghdr, flags));
138d9f0492fSopenharmony_ci    BEGET_ERROR_CHECK(rc >= 0, return NULL, "Failed to get fds from remote, err = %d", errno);
139d9f0492fSopenharmony_ci    return GetFdsFromMsg(outFdCount, requestPid, msghdr);
140d9f0492fSopenharmony_ci}